Coder Social home page Coder Social logo

blog's People

Contributors

buddywang avatar

Watchers

 avatar

blog's Issues

node笔记-全局对象和全局变量

全局变量

__filename

__filename 表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。

__dirname

__dirname 表示当前执行脚本所在的目录。

process

它用于描述当前Node.js 进程状态的对象,提供了一个与操作系统的简单接口。下面是一些常用的成员方法:

事件

1 exit当进程准备退出时触发。
2 beforeExit当 node 清空事件循环,并且没有其他安排时触发这个事件。通常来说,当没有进程安排时 node 退出,但是 'beforeExit' 的监听器可以异步调用,这样 node 就会继续执行。
3 uncaughtException当一个异常冒泡回到事件循环,触发这个事件。如果给异常添加了监视器,默认的操作(打印堆栈跟踪信息并退出)就不会发生。
4 Signal 事件当进程接收到信号时就触发。信号列表详见标准的 POSIX 信号名,如 SIGINT、SIGUSR1 等。

属性

序号. 属性 & 描述
1 stdout标准输出流。
2 stderr标准错误流。
3 stdin标准输入流。
4 argvargv 属性返回一个数组,由命令行执行脚本时的各个参数组成。它的第一个成员总是node,第二个成员是脚本文件名,其余成员是脚本文件的参数。
5 execPath返回执行当前脚本的 Node 二进制文件的绝对路径。
6 execArgv返回一个数组,成员是命令行下执行脚本时,在Node可执行文件与脚本文件之间的命令行参数。
7 env返回一个对象,成员为当前 shell 的环境变量
8 exitCode进程退出时的代码,如果进程优通过 process.exit() 退出,不需要指定退出码。
9 versionNode 的版本,比如v0.10.18。
10 versions一个属性,包含了 node 的版本和依赖.
11 config一个包含用来编译当前 node 执行文件的 javascript 配置选项的对象。它与运行 ./configure 脚本生成的 "config.gypi" 文件相同。
12 pid当前进程的进程号。
13 title进程名,默认值为"node",可以自定义该值。
14 arch当前 CPU 的架构:'arm'、'ia32' 或者 'x64'。
15 platform运行程序所在的平台系统 'darwin', 'freebsd', 'linux', 'sunos' 或 'win32'
16 mainModulerequire.main 的备选方法。不同点,如果主模块在运行时改变,require.main可能会继续返回老的模块。可以认为,这两者引用了同一个模块。

方法

序号 方法 & 描述
1 abort()这将导致 node 触发 abort 事件。会让 node 退出并生成一个核心文件。
2 chdir(directory)改变当前工作进程的目录,如果操作失败抛出异常。
3 cwd()返回当前进程的工作目录
4 exit([code])使用指定的 code 结束进程。如果忽略,将会使用 code 0。
5 getgid()获取进程的群组标识(参见 getgid(2))。获取到得时群组的数字 id,而不是名字。注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。
6 setgid(id)设置进程的群组标识(参见 setgid(2))。可以接收数字 ID 或者群组名。如果指定了群组名,会阻塞等待解析为数字 ID 。注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。
7 getuid()获取进程的用户标识(参见 getuid(2))。这是数字的用户 id,不是用户名。注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。
8 setuid(id)设置进程的用户标识(参见setuid(2))。接收数字 ID或字符串名字。果指定了群组名,会阻塞等待解析为数字 ID 。注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。
9 getgroups()返回进程的群组 iD 数组。POSIX 系统没有保证一定有,但是 node.js 保证有。注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。
10 setgroups(groups)设置进程的群组 ID。这是授权操作,所以你需要有 root 权限,或者有 CAP_SETGID 能力。注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。
11 initgroups(user, extra_group)读取 /etc/group ,并初始化群组访问列表,使用成员所在的所有群组。这是授权操作,所以你需要有 root 权限,或者有 CAP_SETGID 能力。注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。
12 kill(pid[, signal])发送信号给进程. pid 是进程id,并且 signal 是发送的信号的字符串描述。信号名是字符串,比如 'SIGINT' 或 'SIGHUP'。如果忽略,信号会是 'SIGTERM'。
13 memoryUsage()返回一个对象,描述了 Node 进程所用的内存状况,单位为字节。
14 nextTick(callback)一旦当前事件循环结束,调用回调函数。
15 umask([mask])设置或读取进程文件的掩码。子进程从父进程继承掩码。如果mask 参数有效,返回旧的掩码。否则,返回当前掩码。
16 uptime()返回 Node 已经运行的秒数。
17 hrtime()返回当前进程的高分辨时间,形式为 [seconds, nanoseconds]数组。它是相对于过去的任意事件。该值与日期无关,因此不受时钟漂移的影响。主要用途是可以通过精确的时间间隔,来衡量程序的性能。你可以将之前的结果传递给当前的 process.hrtime() ,会返回两者间的时间差,用来基准和测量时间间隔。

参考

Vue 注意相关

更新数组和对象问题

对象

Vue 无法检测 property 的添加或移除

  • 可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式 property,当添加多个 property 时,在这种情况下,你应该用原对象与要混合进去的对象的 property 一起创建一个新的对象
// 添加单个 property
this.$set(this.someObject,'b',2)

// 添加多个 property
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

数组

Vue 不能检测以下数组的变动:

    1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
    • 解决方法:
    vm.$set(vm.items, indexOfItem, newValue)
    
    1. 当你修改数组的长度时,例如:vm.items.length = newLength
    • 解决方法:
    // 索引大于或等于 newLength 的被删除
    vm.items.splice(newLength)
    

自定义指令

插件

插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:

  • 添加全局方法或者 property。如:vue-custom-element
  • 添加全局资源:指令/过滤器/过渡等。如 vue-touch
  • 通过全局混入来添加一些组件选项。如 vue-router
  • 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
  • 一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router
MyPlugin.install = function (Vue, options) {
  // 1. 添加全局方法或 property
  Vue.myGlobalMethod = function () {
    // 逻辑...
  }

  // 2. 添加全局资源
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 逻辑...
    }
    ...
  })

  // 3. 注入组件选项
  Vue.mixin({
    created: function () {
      // 逻辑...
    }
    ...
  })

  // 4. 添加实例方法
  Vue.prototype.$myMethod = function (methodOptions) {
    // 逻辑...
  }
}

nextTick 理解

nextTick 把回调函数放到本次事件循环的 microtask 队列中去

css坑!height 使用百分比单位时注意

当元素 height 设置百分百单位时,根据 CSS2.1 的定义:

Specifies a percentage height. The percentage is calculated with respect to the height of the generated box's containing block. If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned, the value computes to 'auto'.

如果包含该元素的容器没有指定一个确定的高度(min/max-height指定的高度是不确定的),且该元素不是绝对定位,那么 height 会被计算为 auto (由浏览器计算选择一个高度)

参考

babel插件

问题

  • babel插件是干什么的
  • babel插件长什么样子的
  • 怎么写
  • 怎么使用

babel 流程(转译器流程)

  • parse:解析代码生成 ast

  • transform:遍历 ast(访问者模式),对 ast 做增删改

    • 各种转译插件也是在这一步骤做操作;

      代码转换功能以插件的形式出现,插件是小型的 JavaScript 程序,用于指导 Babel 如何对代码进行转换。

    • 访问者是一个用于 AST 遍历的跨语言的模式。简单的说它们就是一个对象,定义了用于在一个树状结构中获取具体节点的方法。

      const MyVisitor = {
        Identifier: {
            enter() {
            console.log("Entered!");
            },
            exit() {
            console.log("Exited!");
            }
        }
      };
      
      // 每当在树中遇见一个 Identifier 的时候会调用 Identifier() 方法
  • generate:打印 ast(生成最终代码)并生成source maps,每种 node 会对应一个打印方法

转译插件开发思路

  1. 确定我们需要处理的节点类型
  2. 处理节点
  3. 返回新的节点
// 插件长这样
// export default function(babel) {
export default function({types: t}) {
  return {
    visitor: {
      Identifier(path, state) {},
      ASTNodeTypeHere: {
        enter(path, state) {},
        exit() {}
      }
    }
  };
};

数据结构

node

ast 由 node 组成

Paths(路径)

Path 是表示两个节点之间连接的对象,在某种意义上,Path 是一个节点在树中的位置以及关于该节点各种信息的响应式 Reactive 表示。

{
  "parent": {...},
  "node": {...},
  "contexts": [],
  "data": {},
  "removed": false,
  "state": null,
  "parentPath": null,
  "context": null,
  "container": null,
  "parentKey": null,
  "key": null,
  "scope": null,
  "type": null,
  "typeAnnotation": null,
  ...
}

通常通过操作 path 来更改 node

const MyVisitor = {
  Identifier(path) {
    console.log("Visiting: " + path.node.name);
  }
};

Scopes(作用域)

  • 跟 JavaScript 里的作用域类似,在 JavaScript 中,每当你创建了一个引用,不管是通过变量(variable)、函数(function)、类型(class)、参数(params)、模块导入(import)还是标签(label)等,它都属于当前作用域。
  • 更深的内部作用域代码可以使用外层作用域中的引用。
  • 内层作用域也可以创建和外层作用域同名的引用。
{
  path: path,
  block: path.node,
  parentBlock: path.parent,
  parent: parentScope,
  bindings: [...],
  ...
}

Bindings(绑定)

所有引用属于特定的作用域,引用和作用域的这种关系被称作:绑定(binding)。单个绑定看起来像这样︰

{
  identifier: node,
  scope: scope,
  path: path,
  kind: 'var',

  referenced: true,
  references: 3,
  referencePaths: [path, path, path],

  constant: false,
  constantViolations: [path],
  ...
}

有了这些信息你就可以查找一个绑定的所有引用,并且知道这是什么类型的绑定(参数,定义等等),查找它所属的作用域,或者拷贝它的标识符。你甚至可以知道它是不是常量,如果不是,那么是哪个路径修改了它。

babel提供的工具包

工具包 功能
@babel/parser babel里的JavaScript解析器,用于解析代码生成ast
@babel/core babel的核心功能的组合封装,例如将parser、traverse、generator功能组合起来向外提供transform方法
@babel/generator 将ast转换成代码
@babel/code-frame 提供打印定位代码位置的能力,用于显示报错提示
@babel/runtime 一个包含运行时 helper 方法和一个 regenerator-runtime 的库,不常用,一般在 @babel/plugin-transform-runtime 里被调用
@babel/template 从字符串模板生成ast
@babel/traverse 遍历ast
@babel/types 提供构建ast节点和验证ast节点的能力

例子

下面的插件例子功能是把代码里 console.xxx 输出日志的语句删除,第一位参数是 "do not move" 的保留:

const simpleDeleteLogPlugin = ({ types, template }, options, dirname) => {
    return {
        visitor: {
            CallExpression(path, state) {
                const calleeName = path.get('callee').toString();
                const arg = path.node.arguments
                if (calleeName.indexOf('console') !== -1) {
                    if (arg.length > 0 && types.isStringLiteral(arg[0], { value: "do not move" })) {
                        // do not move
                    } else {
                        path.remove()
                    }
                }
            }
        }
    }
}

module.exports = simpleDeleteLogPlugin;

使用结果:

const { transformFromAstSync } = require('@babel/core');
const parser = require('@babel/parser');
const simpleDeleteLogPlugin = require('./plugin/demo-plugin');

const sourceCode = `
    console.log(console.log(123))
    function a() {
        const obj = { b: 2 }
        console.log(obj);
        return obj
    }
    const c = new Promise()
    const b = a()
    console.log('do not move', b)`

const ast = parser.parse(sourceCode, {
    sourceType: 'unambiguous'
});

const { code } = transformFromAstSync(ast, sourceCode, {
    plugins: [
        simpleDeleteLogPlugin
    ]
});

console.log(code);

// 输出结果
// require("core-js/modules/es.object.to-string.js");

// require("core-js/modules/es.promise.js");

// function a() {
//   var obj = {
//     b: 2
//   };
//   return obj;
// }

// var c = new Promise();
// var b = a();
// console.log('do not move', b);

参考

standard-version:版本控制、生成 changelog、打 tag

工具

standard-version

生成 changelog 前提

你的 commit message 符合规范概括

安装

# 全局安装
npm i -g standard-version

使用

JS 项目

发布一个版本

运行

standard-version
# 根据最后一个 tag 生成版本(默认更新补丁版本),没有 tag 则生成版本 v1.0.0

上面命令会执行下面的内容:

    1. 查看 package.json/package-lock.json/bumpfiles 里的版本信息(使用最后一个 git tag)确认当前的版本
    1. 更新 package.json/package-lock.json/bumpfiles 里的版本信息(默认更新补丁版本)
    1. 根据你的 commit message 生成/更新 CHANGELOG.md(包含bugfix、feature、breaking change三部分)
    1. 新增一个提交,用来更新 package.json/package-lock.json/bumpfiles 和 CHANGELOG.md
    1. 根据版本号打一个 tag

注意:发布版本会新增一个提交,为了避免触发 git hooks,可以在命令后加上 --no-verify 参数

自定义发布版本号

假设现在版本是 v1.0.0,运行 standard-version 命令会默认增加补丁版本,即生成版本 v1.0.1,你可以通过以下命令来自定义版本号:

standard-version --release-as 1.1.0
# 打 tag 时默认在版本号前加 v,更改这个默认值可以运行 `standard-version -t <new-prefix>`

发布一个预发布版本

假设现在版本是 v1.0.0,运行命令:

standard-version --prerelease
# 这会发布一个 v1.0.0-0 的版本

# 也可以为预发布版本起名字
standard-version --prerelease alpha
# 这会发布一个 v1.0.0-alpha.0 的版本

非 JS 项目

    1. 安装 node.js (包含 npx 命令)
    1. 全局安装 standard-version,npm i -g standard-version
    1. 在上面的命令前加上 npx 运行即可

另外

  • 运行 standard-version 只会更改本地仓库的内容,不会影响远程仓库
  • semantic-release 比 standard-version 多提供了不仅仅 发布版本后自动 push 到远程仓库的功能

Git-flow

image

  • master:跟踪线上的版本,每个提交对应一个 tag
  • hotfix (from master):用来修复线上问题,改完要合并到 master 和 develop 分支
  • develop(from master):主要开发分支
  • release(预发布 from develop):包含下个release的代码,用来验证测试改bug的,验证改完 bug 后要合并到 develop 和 master 分支
  • feature(from develop):新增功能开发分支,开发完合并到 develop 分支

webpack相关

配置之 Resolve

该选项用来改变模块是怎么被解析的

module.exports = {
  //...
  resolve: {
    // configuration options
  }
}

resolve.alias

创建别名让导入某些模块更加简短(相当于一个字符串变量,编译时替换)

const path = require('path');

module.exports = {
  //...
  resolve: {
    alias: {
      'Util': path.resolve(__dirname, 'src/utilities/')
    }
  }
};

原来的导入

import Utility from '../../utilities/utility';

可以简单用以下表示,注意路径

import Utility from 'Util/utility';

TypeScript相关

是什么和为什么

TypeScript 是 JavaScript 多一个超集,主要提供了类型系统和对 ES6 的支持,它由 Microsoft 开发。用 TypeScript 具有不仅于以下好处:

  • 增加代码多可读性和可维护性
  • 可以在编译阶段就发现大部分错误
  • 增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等

常用用法

为变量声明类型

let t1: boolean = false;
let t2: number = 1;
let t3: string = 'asdf';
let t4: null = null;
let t5: undefined = undefined;
// any 表示任何类型,对该类型值的任何操作会逃过类型检查,尽量不要用,需要时用 unknown 类型替代
let t6: any; 
// 对照于any,unknown 是类型安全的。 任何值都可以赋给 unknown,但是当没有类型断言或基于控制流的类型细化时 unknown 不可以赋值给其它类型
let t7: unknown; 

// 联合类型,当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法
let t8: string | number;
t8. length // length 不是 string 和 number 的共有属性,报错
if(typeof t8 === 'string'){
  t8.length; // 不会报错
}

// 定义函数,定义参数类型和返回值类型
function t9(a: number = 1, b?: number): void { // ? 表示可选参数,带默认值的参数 ts 也认为其是可选参数,void 表示函数没有返回值
  ...
}
// 函数表达式的定义
t9: (a: number, b?: number) => void = function (a: number, b?: number) {
  ...
}
// 函数重载(可以用泛型实现)
function t9(x: number): number;
function t9(x: string): string;
function t9(x: number | string): number | string {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}

// 定义数组
let t10: number[] = [1, 2, 4];
  // 或者用数组泛型
let t11: Array<number> = [1, 2, 3];

// 定义对象的类型,用 interface 描述对象的形状
interface It12 { // 建议接口的名称加上 I 前缀
  readonly id: number; // readonly 定义只读属性
  name: string;
  age?: number; // 可选属性
  [propName: string]: any; // 任意属性,确定属性和可选属性的类型都必须是它的类型的子集,一个接口中只能定义一个任意属性。如果接口中有多个类型的属性,则可以在任意属性中使用联合类型
}
// 赋值的时候,变量的形状必须和接口的形状保持一致,否则报错
let t13: It12 = { // 缺少属性,报错
  name: 'tom',
}

// 枚举,枚举成员会被赋值为从 0 开始递增的数字,Days.Sun = 0
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};

类型断言

手动指定一个值的类型,语法是

值 as 类型
用途:

  • 将一个联合类型断言为其中一个类型
  • 将一个父类断言为更加具体的子类
  • 将任何一个类型断言为 any,慎用
window.foo = 1 // 报错
(window as any).foo = 1 // ok
  • 将 any 断言为一个具体的类型
  • type
// 类型别名,用来给一个类型起个新名字
type Name = string;
let a: Name = 'sss';

// 字符串字面量类型,约束取值范围
type EventNames = 'click' | 'scroll' | 'mousemove';
let event: EventNames; // event 只能取 'click' | 'scroll' | 'mousemove' 其中之一

类与接口

interface 也可以用来对类的一部分行为进行抽象,然后类可以实现(implements)接口,例如,对于防盗门和车,都可以有报警器多功能,这时可以把报警器提取出来,作为一个接口,防盗门和车都去实现它:

interface Alarm {
    alert(): void;
}

class Door {
}

class SecurityDoor extends Door implements Alarm {
    alert() {
        console.log('SecurityDoor alert');
    }
}

class Car implements Alarm {
    alert() {
        console.log('Car alert');
    }
}

注意:
类定义会创建两个东西:类的实例类型和一个构造函数。 因为类可以创建出类型,所以你能够在允许使用接口的地方使用类,例如:

  • 一个类可以实现多个接口
  • 接口之间可以继承
  • 接口可以继承类
class Point {
    x: number;
    y: number;
}

// Point 类定义创建了 Point 类型
interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};

泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

// 函数使用泛型
function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]];
}
swap<number, string>([7, 'seven']); // ['seven', 7]
  // 也可以不指定<T, U>,而让类型推论自动推算出来
swap([7, 'seven']); // ['seven', 7]
// 泛型约束:对泛型使用 extends,可以对泛型进行约束
interface Lengthwise {
    length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}
loggingIdentity(7);
// index.ts(10,17): error TS2345: Argument of type '7' is not assignable to parameter of type 'Lengthwise'.

// 接口使用泛型
interface CreateArrayFunc<T> {
    (length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc<any>;
createArray = function<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']

// 类使用泛型
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

声明文件

参考

Node相关

path 模块

用于处理文件和目录的路径。
注意

  • 区分系统写法,针对window系统的 path.win32 和 POSIX系统的 path.posix

path.delimiter

提供平台特定的路径定界符:

  • ; 用于 Windows
  • : 用于 POSIX
// POSIX 上
console.log(process.env.PATH);
// 打印: '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin'

process.env.PATH.split(path.delimiter);
// 返回: ['/usr/bin', '/bin', '/usr/sbin', '/sbin', '/usr/local/bin']
-----------
// windows 上
console.log(process.env.PATH);
// 打印: 'C:\Windows\system32;C:\Windows;C:\Program Files\node\'

process.env.PATH.split(path.delimiter);
// 返回: ['C:\\Windows\\system32', 'C:\\Windows', 'C:\\Program Files\\node\\']

path.sep

提供平台特定的路径片段分隔符:

  • Windows 上是 \
  • POSIX 上是 /
'foo/bar/baz'.split(path.sep);
// 返回: ['foo', 'bar', 'baz']

'foo\\bar\\baz'.split(path.sep);
// 返回: ['foo', 'bar', 'baz']

path.join([...paths])

将所有给定的 path 片段连接到一起(使用平台特定的分隔符作为定界符),然后规范化生成的路径

path.join('/目录1', '目录2', '目录3/目录4', '目录5', '..');
// 返回: '/目录1/目录2/目录3/目录4'

path.resolve([...paths])

将路径或路径片段的序列解析为绝对路径

  • 从右到左进行处理,后面的每个 path 会被追加到前面,直到构造出绝对路径即返回
  • 如果在处理完所有给定的 path 片段之后还未生成绝对路径,则追加当前工作目录到前面
  • 传空返回当前工作目录的绝对路径
path.resolve('/目录1/目录2', './目录3');
// 返回: '/目录1/目录2/目录3'

path.resolve('/目录1/目录2', '/目录3/目录4/');
// 返回: '/目录3/目录4'

path.resolve('目录1', '目录2/目录3/', '../目录4/文件.gif');
// 如果当前工作目录是 /目录A/目录B,
// 则返回 '/目录A/目录B/目录1/目录2/目录4/文件.gif'

path.dirname(path)

返回 path 的目录名

path.dirname('/目录1/目录2/目录3');
// 返回: '/目录1/目录2'

path.format(pathObjec) 和 path.parse(path)

  • pathObject 包括 dir、root、base、name、ext 属性
  • 如果提供了 pathObject.dir,则忽略 pathObject.root。
  • 如果 pathObject.base 存在,则忽略 pathObject.ext 和 pathObject.name。
    前者从对象返回路径字符串。 与 path.parse() 相反
path.parse('/目录1/目录2/文件.txt');
// 返回:
// { root: '/',
//   dir: '/目录1/目录2',
//   base: '文件.txt',
//   ext: '.txt',
//   name: '文件' }

element 和 mouseEvent 的位置和尺寸信息相关

Element

属性

clientHeight/Width

它是**元素内部(可见部分)**的高度/宽度(单位像素),包含内边距,但不包括水平滚动条、边框和外边距

clientTop/Left

表示一个元素的上边框/左边框(border width)的宽度

scrollTop/Left

一个元素的 scrollTop 值是这个元素的内容顶部(考虑滚动不可见的)到它的视口可见内容(的顶部)的距离的度量。当一个元素的内容没有产生垂直方向的滚动条,那么它的 scrollTop 值为0。scrollLeft同理(可为负值)

scrollHeight/Width

它是元素内容高度/宽度的一种度量,包括由于overflow溢出而在屏幕上不可见的内容

  • 判定元素是否滚动到底
    如果元素滚动到底,下面等式返回true,没有则返回false
element.scrollHeight - element.scrollTop === element.clientHeight

方法

getBoundingClientRect()

该方法返回元素的大小及其相对于视口(viewport)的位置:left, top, right, bottom, x, y, width, 和 height(可以是负值)

mouseEvent

属性

clientX/Y

表示相对于视口的位置信息,不管有没有滚动

offsetX/Y

表示事件对象与目标节点的内填充边(padding edge)在位置偏移量

pageX/Y

表示相对于整个文档的位置信息,考虑任何滚动

screenX/Y

表示相对于屏幕的位置信息

参考

js模块化历史

ES6 之前常用的是 CommonJs 和 AMD(Asynchronous Module Definition)模块规范,后面为了支持这两种模块规范,UMD 模块规范出现了,它同时支持 AMD 和 CommonJS 规范的模块,再后面 ES6 支持原生模块,又替代了 UMD 模块

  • CommonJs 模块用于服务器端,模块是同步加载,Node.js 使用 CommonJs 规范
// 定义模块,myModule.js
function myModule() {
  this.hello = function() {
    return 'hello!';
  }

  this.goodbye = function() {
    return 'goodbye!';
  }
}

module.exports = myModule;


// 加载模块,b.js
var myModule = require('myModule');

var myModuleInstance = new myModule();
myModuleInstance.hello(); // 'hello!'
myModuleInstance.goodbye(); // 'goodbye!'
  • AMD 规范用于浏览器端,可以异步加载模块, Require.js 使用 AMD 规范
// 定义模块,myModule.js
// 第一个参数是依赖的模块数组,第二个参数是返回模块输出的函数
define([], function() {
  return {
    hello: function() {
      console.log('hello');
    },
    goodbye: function() {
      console.log('goodbye');
    }
  };
});


// 加载模块,b.js
require(['myModule'], function (myModule){
 myModule.hello('hi');
});
  • UMD(Universal Module Definition)规范同时支持上面两种模块(CommJs 和 AMD 都可以导入 UMD 模块)
// 定义模块,根据环境来返回对应的模块定义
(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
      // AMD
    define(['myModule', 'myOtherModule'], factory);
  } else if (typeof exports === 'object') {
      // CommonJS
    module.exports = factory(require('myModule'), require('myOtherModule'));
  } else {
    // Browser globals (Note: root is window)
    root.returnExports = factory(root.myModule, root.myOtherModule);
  }
}(this, function (myModule, myOtherModule) {
  // Methods
  function notHelloOrGoodbye(){}; // A private method
  function hello(){}; // A public method because it's returned (see below)
  function goodbye(){}; // A public method because it's returned (see below)

  // Exposed public methods
  return {
      hello: hello,
      goodbye: goodbye
  }
}));
  • ES6 模块
// 定义模块,counter.js
export let counter = 1;

export function increment() {
  counter++;
}

export function decrement() {
  counter--;
}


// 加载模块,b.js
import * as counter from './counter';

console.log(counter.counter); // 1
counter.increment();
console.log(counter.counter); // 2

参考

DOMContentLoaded 与 load、defer 与 async

DOMContentLoaded 与 load

DOMContentLoaded

  • 当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完全加载
  • 与白屏时间相关
  • 注意:同步 JS 会中断 HTML 的解析(defer...)

load

当所有资源加载完成之后,load 事件才会被触发

defer 与 async

defer

  • 异步加载脚本
  • 加载完成后等待 HTML 解析完成后执行,执行完成后触发 DOMContentLoaded
  • 不同脚本执行顺序按照代码顺序

async

  • 异步加载脚本
  • 加载完成后中断 HTML 解析,立即执行;HTML 解析完成后触发 DOMContentLoaded
  • 不同脚本执行顺序不确定

参考

https://github.com/CompileYouth/front-end-study/blob/master/html/domcontentloaded/DOMContentLoaded.md

yarn与npm区别

yarn 相对 npm

  1. 更快
  2. 版本确定( npm5 出来后,这个也不算优点了)
  3. 命令更容易理解
  4. 控制台信息更简洁

yarn 命令

命令 说明
yarn init 初始化
yarn add [package][@[version/tag])] 添加依赖包
yarn add [package] --dev(peer/optional) 添加依赖包到不同依赖项中
yarn upgrade [package][@[version/tag]] 升级依赖包
yarn remove [package] 移除依赖包
yarn 安装项目全部依赖

调试 JS

调试的几种方法

1. console 常用方法 👍

log

向控制台输出一条信息

time & timeEnd

启动一个计时器(timer)来跟踪某一个操作的占用时长

console.time('getData')
.... // getData
console.timeEnd('getData')

// getData: 0.5546875 ms

assert

它第一个参数接受一个断言(声明), 第二个参数是一个 message。如果断言为 false,则将 message 写入控制台。如果断言是 true,则忽略

......
console.assert(num===2, 'num should be 3')

// 如果 num 不等于 2,则控制台输出错误
// Assertion failed: num should be 3

2. Chrome Dev tool/source 👍

关于 Chrome 调试面板 source 的使用

3. VS Code + Debugger for Chrome

    1. 运行你的项目
    1. 安装 Debugger for Chrome 插件
    1. 添加 launch.json 文件,选择 Chrome: luanch
      image
    1. 检查 url(运行的项目的 url,用于新开窗口导航到你的项目) 和 webRoot(项目运行的目录) 是否正确
    1. 开始 debug,添加断点
      image

参考

vue-cli使用相关

安装

npm install -g @vue/cli
# OR
yarn global add @vue/cli

vue serve 快速原型开发

先安装一个全局的扩展

npm install -g @vue/cli-service-global

使用

Usage: serve [options] [entry]

在开发环境模式下零配置为 .js 或 .vue 文件启动一个服务器

Options:
  -o, --open  打开浏览器
  -c, --copy  将本地 URL 复制到剪切板
  -h, --help  输出用法信息

例如对于一个 demo.vue 文件,在该目录下运行 vue serve demo.vue 即可快速查看效果

创建一个项目

运行

用法:vue create [options] <app-name>

创建一个由 `vue-cli-service` 提供支持的新项目


选项:

  -p, --preset <presetName>       忽略提示符并使用已保存的或远程的预设选项
  -d, --default                   忽略提示符并使用默认预设选项
  -i, --inlinePreset <json>       忽略提示符并使用内联的 JSON 字符串预设选项
  -m, --packageManager <command>  在安装依赖时使用指定的 npm 客户端
  -r, --registry <url>            在安装依赖时使用指定的 npm registry
  -g, --git [message]             强制 / 跳过 git 初始化,并可选的指定初始化提交信息
  -n, --no-git                    跳过 git 初始化
  -f, --force                     覆写目标目录可能存在的配置
  -c, --clone                     使用 git clone 获取远程预设选项
  -x, --proxy                     使用指定的代理创建项目
  -b, --bare                      创建项目时省略默认组件中的新手指导信息
  -h, --help                      输出使用帮助信息

然后根据需要按提示选择对应的功能即可

CLI服务

package.json

{
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build"
  }
}

运行项目

用法:vue-cli-service serve [options] [entry]

选项:

  --open    在服务器启动时打开浏览器
  --copy    在服务器启动时将 URL 复制到剪切版
  --mode    指定环境模式 (默认值:development)
  --host    指定 host (默认值:0.0.0.0)
  --port    指定 port (默认值:8080)
  --https   使用 https (默认值:false)

构建项目

用法:vue-cli-service build [options] [entry|pattern]

选项:

  --mode        指定环境模式 (默认值:production)
  --dest        指定输出目录 (默认值:dist)
  --modern      面向现代浏览器带自动回退地构建应用 👍 
  --target      app | lib | wc | wc-async (默认值:app)
  --name        库或 Web Components 模式下的名字 (默认值:package.json 中的 "name" 字段或入口文件名)
  --no-clean    在构建项目之前不清除目标目录
  --report      生成 report.html 以帮助分析包内容
  --report-json 生成 report.json 以帮助分析包内容
  --watch       监听文件变化

静态资源

  • 在 JavaScript 被导入或在 template/CSS 中通过相对路径被引用。这类引用会被 webpack 处理。这样有以下好处: 👍
    • 脚本和样式表会被压缩且打包在一起,从而避免额外的网络请求。
    • 文件丢失会直接在编译时报错,而不是到了用户端才产生 404 错误。
    • 最终生成的文件名包含了内容哈希,因此你不必担心浏览器会缓存它们的老版本。(如果想要缓存,一种方法是可以放在public目录下)
  • 放置在 public 目录下或通过绝对路径被引用。这类资源将会直接被拷贝,而不会经过 webpack 的处理。
  • 任何放置在 public 文件夹的静态资源都会被简单的复制,而不经过 webpack。你需要通过绝对路径来引用它们。

何时使用 public 文件夹

  • 你需要在构建输出中指定一个文件的名字。
  • 你有上千个图片,需要动态引用它们的路径。
  • 有些库可能和 webpack 不兼容,这时你除了将其用一个独立的 <script> 标签引入没有别的选择。

环境变量和模式

环境变量

可通过下列文件来指定环境变量:

.env                # 在所有的环境中被载入
.env.local          # 在所有的环境中被载入,但会被 git 忽略
.env.[mode]         # 只在指定的模式中被载入
.env.[mode].local   # 只在指定的模式中被载入,但会被 git 忽略

文件内容是“键=值”对 eg:

FOO=bar
VUE_APP_SECRET=secret

模式 mode

默认情况下,一个 Vue CLI 项目有三个模式:

  • development 模式用于 vue-cli-service serve
  • production 模式用于 vue-cli-service build 和 vue-cli-service test:e2e
  • test 模式用于 vue-cli-service test:unit

在客户端侧代码中使用环境变量

NODE_ENVBASE_URL 外,以 VUE_APP_ 开头的变量会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中,这样可以在代码中使用它们:

console.log(process.env.VUE_APP_SECRET)

Vue styleguidist相关

Tags

@values

如果一个prop取值有限制,可以用它来说明

export default = {
    props: {
      /**
       * The size of the button
       * @values small, normal, large
       */
      size: {
        type: String,
        default: 'normal'
      }
    }
}

@deprecated

不推荐使用的prop,有删除线效果

/**
 * An example-less button.
 * @deprecated Use the [only true button component](#button) instead
 */

@ since

指定该属性/方法/类在特定版本新增的

export default = {
    props: {
      /**
       * The size of the button
       * @values small, normal, large
       * @since 1.0.1
       */
      size: {
        type: String,
        default: 'normal'
      }
    }
}

@ignore

忽略该prop,在文档上不显示

    props: {
      /**
       * The size of the button
       * @ignore
       */
      size: {
        type: String,
        default: 'normal'
      }
    }

修饰方法

  • @param:参数说明 @param {string} somebody Somebody's name. (类型、名字、描述)
  • @event:说明在文档上的事件名字 @event click (一个名为click的事件)
  • @public:在文档上显示该方法

Git 常用命令

本地相关

# 查看所有配置
git config --list

# 设置用户名和邮箱
git config --global user.name youname
git config --global user.email youremail

# 初始化本地仓库
git init

# 查看对比上一次 commit 的未暂存的修改,[]为可选参数
git diff [path to you file]
# 查看对比上一次 commit 的已暂存的修改
git diff --staged [path to you file]
# 将会找到可能的空白错误并将它们为你列出来,通常提交空白会让人气愤
git diff --check

# 将修改添加到暂存区,. 表示暂存所有修改
git add ./(path to you file)

# 取消修改/暂存,查看运行 git status 和 git add 命令后的提示
# 取消修改,对untrack的文件无效,因为没有历史记录
git checkout -- <file-name>
# 取消暂存
git reset HEAD -- [file-name]

# 将暂存区的修改提交,commit message 应遵循以下规范
git commit -m "commit message"

commit message 规范:type(scope):subject

# type:
# # 主要type
# feat:     增加新功能
# fix:      修复bug

# # 特殊type
# docs:     只改动了文档相关的内容
# style:    不影响代码含义的改动,例如去掉空格、改变缩进、增删分号
# build:    构造工具的或者外部依赖的改动,例如webpack,npm
# refactor: 代码重构时使用
# revert:   执行git revert打印的message

# scope:描述改动的范围,一般是具体分支名或更具体的目录模块

# subject:commit 的目的和具体信息描述,一般动词开头

# eg:在 release-0930 分支添加一个弹窗功能
# git commit -m "feat(release-0930): 添加...弹窗功能"

查看提交历史

# 精简查看提交历史
git log --pretty=oneline
# 形象地展示你分支、合并历史
git log --pretty=format:"%h %an %s" --graph

# 修改上一次提交,用于修改提交信息 或 合并暂存区的修改到上一个提交
git commit --amend

分支管理

# 创建并切换分支
git checkout -b <branch-name>

# 合并其它分支到当前分支
git merge <branch-name>

# 删除分支
git branch -d <branch-name>

# 查看每一个分支的最后一次提交
git branch -v
# 比 -v 命令多了跟踪分支的信息等
git branch -vv

# 推送本地新的分支到远程
git push <remote-name> <branch-name>

# 删除远程的分支,短时间内可以通过hash恢复
git push <remote-name> --delete <branch-name>

远程相关

生成 SSH 公钥

ssh-keygen -o

然后

# 添加远程仓库
git remote add <shortname> <url>
# 查看远程仓库信息(name, url)
git remote -v
# 查看仓库详细信息,包括远程本地分支信息,本地分支与远程的关联等信息
git remote show <remote name>

# 更新远程分支信息到本地
git remote update origin --prune
# 查看远程分支信息(更新远程分支时用上面命令)
git branch -r

#拉取远程分支并切换,并且跟踪分支
git checkout -b 本地分支 origin/远程分支

打标签

# 附注标签
git tag -a <tag-name> [-m <tag-message>] [hashid]

# 轻量标签
git tag  tag-name [hashid]

# 查看所有标签名
git tag
# 查看标签具体信息
git show <tag-name>

# 推送所有标签到远程
git push <remote-name> --tags
# 推送特定标签到远程
git push <remote-name> <tag-name>

# 本地删除标签
git tag -d <tag-name>
# 远程删除标签
git push <remote-name> --delete <tag-name>

git 别名(提高效率)

# 配置别名,xx 是别名,<git-command> 为你想起别名的命令,多个单词加引号
git config --global alias.xx <git-command>
eg: 
git config --global alias.ci commit
git config --global alias.unstage 'reset HEAD --'

进度

7.7

参考

git

用class+ts编写vue组件:vue-property-decorator相关

用法

@component:表示一个vue组件

  • data、methods、生命周期、render:声明为类属性
  • 计算属性:声明为类属性的 getter/setter
  • 其它 options(选项/**),可作为参数传给 @component 或看后面的用法
import {Vue, Component} from 'vue-property-decorator';
import OtherComponent from './OtherComponent.vue'

@Component({
    components: {
        OtherComponent
    }
})
export default class "组件名" extends Vue{
    value1: string = 'hello'
    get value2() {...}
    set value2(val: string) {...}
    getData() {...}
    mounted() {...}

}

等同于

export default {
    components: {
        OtherComponent
    },
    data(){
        return {
            value1: 'hello'
        }
    },
    computed: {
        value2: {
            get() {...},
            set() {...}
        }
    },
    methods: {
        getData() {...}
    },
    mounted() {...}
}

@prop(options: (PropOptions | Constructor[] | Constructor) = {})

import { Vue, Component, Prop } from 'vue-property-decorator'
 
@Component
export default class YourComponent extends Vue {
  @Prop(Number) readonly propA: number | undefined
  @Prop({ default: 'default value' }) readonly propB!: string
  @Prop([String, Boolean]) readonly propC: string | boolean | undefined
}

等同于

export default {
  props: {
    propA: {
      type: Number,
    },
    propB: {
      default: 'default value',
    },
    propC: {
      type: [String, Boolean],
    },
  },
}

@ProPsync(propName: string, options: (PropOptions | Constructor[] | Constructor) = {})

import { Vue, Component, PropSync } from 'vue-property-decorator'
 
@Component
export default class YourComponent extends Vue {
  @PropSync('name', { type: String }) syncedName!: string
}

等同于

export default {
  props: {
    name: {
      type: String,
    },
  },
  computed: {
    syncedName: {
      get() {
        return this.name
      },
      set(value) {
        this.$emit('update:name', value)
      },
    },
  },
}

@watch(path: string, options: WatchOptions = {})

import { Vue, Component, Watch } from 'vue-property-decorator'
 
@Component
export default class YourComponent extends Vue {
  @Watch('child')
  onChildChanged(val: string, oldVal: string) {}
 
  @Watch('person', { immediate: true, deep: true })
  onPersonChanged1(val: Person, oldVal: Person) {}
 
  @Watch('person')
  onPersonChanged2(val: Person, oldVal: Person) {}
}

等同于

export default {
  watch: {
    child: [
      {
        handler: 'onChildChanged',
      },
    ],
    person: [
      {
        handler: 'onPersonChanged1',
        immediate: true, // 默认false
        deep: true, // 默认false
      },
      {
        handler: 'onPersonChanged2',
      },
    ],
  },
  methods: {
    onChildChanged(val, oldVal) {},
    onPersonChanged1(val, oldVal) {},
    onPersonChanged2(val, oldVal) {},
  },
}

@model(event?: string, options: (PropOptions | Constructor[] | Constructor) = {})

import { Vue, Component, Model } from 'vue-property-decorator'
 
@Component
export default class YourComponent extends Vue {
  @Model('change', { type: Boolean }) readonly checked!: boolean
}

等同于

export default {
  model: {
    prop: 'checked',
    event: 'change',
  },
  props: {
    checked: {
      type: Boolean,
    },
  },
}

@emit(event?: string)

import { Vue, Component, Emit } from 'vue-property-decorator'
 
@Component
export default class YourComponent extends Vue {
  count = 0
 
  @Emit()
  addToCount(n: number) {
    this.count += n
  }
 
  @Emit('reset')
  resetCount() {
    this.count = 0
  }
 
  @Emit()
  returnValue() {
    return 10
  }
 
  @Emit()
  onInputChange(e) {
    return e.target.value
  }
 
  @Emit()
  promise() {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(20)
      }, 0)
    })
  }
}

等同于

export default {
  data() {
    return {
      count: 0,
    }
  },
  methods: {
    addToCount(n) {
      this.count += n
      this.$emit('add-to-count', n)
    },
    resetCount() {
      this.count = 0
      this.$emit('reset')
    },
    returnValue() {
      this.$emit('return-value', 10)
    },
    onInputChange(e) {
      this.$emit('on-input-change', e.target.value, e)
    },
    promise() {
      const promise = new Promise((resolve) => {
        setTimeout(() => {
          resolve(20)
        }, 0)
      })
 
      promise.then((value) => {
        this.$emit('promise', value)
      })
    },
  },
}

@provide

@Inject

@ProvideReactive

@InjectReactive

@ref

等同于

webpack最浅了解

**目的就是把分模块开发的各个模块打包到一个(多个)文件,中间可以进行压缩/转译等等操作

  1. 首先需要一个配置文件(解耦合),说明打包入口出口什么的
  2. 入口文件:主要过程是读取入口文件,解析(babylon)生成 ast 语法树,然后进一步分析(babel-traverse)得到模块依赖关系等,然后将这些依赖模块写到一个(多个)文件里(自执行函数)

in 和 hasOwnProperty

in 和 hasOwnProperty

  • prop in obj,用于判断对象 obj 上是否有 prop 属性(包括原型链上的)
  • obj.hasOwnProperty(prop) 是 Object 原型上的方法,用于判断对象 obj 上是否有 prop 属性(忽略原型链上的) 性能好点,但 hasOwnProperty 属性不受保护,可以被覆盖,稳妥的方法是直接调用 Object 原型上的方法 Object.prototype.hasOwnProperty.call(...) 👍

Vuex用法总结

Vuex是Vue.js应用程序开发多状态管理模式,它通过建立一个全局单例来集中储存管理应用多所有组件多状态,并以相应的规则保证状态以一种可预测的方式发生变化。其核心是store仓库,并且是响应式的。
image

使用

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
// demo
const store = new Vuex.Store({
  state: {
    arr: [],
  },
  getters: {
    getlen: state => {
      return state.arr.length
    }
  },
  mutations: {
    addNum (state, payload) {
      state.arr.push(payload.num)
    }
  },
  actions: {
    addNum ({ commit, state, dispatch }) {
      commit('addNum', {num: 1})
    }
  },
  modules: {...},
})

更改state必须通过提交mutation来实现,异步操作写在action里面。

State

在子组件可以通过this.$store.state来访问到
** mapState **辅助函数可以很方便地批量生成计算属性

// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // 箭头函数可使代码更简练
    count: state => state.count,

    // 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: 'count',

    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}

当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。

computed: mapState([
  // 映射 this.count 为 store.state.count
  'count'
])

与局部计算属性混合使用

computed: {
  localComputed () { /* ... */ },
  // 使用对象展开运算符将此对象混入到外部对象中
  ...mapState({
    // ...
  })
}

Getters

相当于store的计算属性,可以通过this.$store.getters访问

getters: {
  // ...
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}

实现getter传参

getters: {
  // ...
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}
-----
this.$store.getters.getTodoById(2)

mapGetters辅助函数将store中多getter映射到局部计算属性

import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
  // 使用对象展开运算符将 getter 混入 computed 对象中,数组参数,getter与计算属性名字相同
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ]),
  // 另取名字,对象参数
    ...mapGetters({
      // 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
      doneCount: 'doneTodosCount'
    })
  }
}

Mutations

专门用来更改store中多状态,可以通过store.commit('mutation-name', payload)来触发,也可以通过对象参数来触发

store.commit({
  type: 'mutation-name',
  payload: {...}
})

注意

    1. 最好提前在你的 store 中初始化好所有所需属性。
    1. 当需要在对象上添加新属性时,你应该
    • 使用 Vue.set(obj, 'newProp', 123), 或者
    • 以新对象替换老对象。例如,利用对象展开运算符我们可以这样写:state.obj = { ...state.obj, newProp: 123 }
      mapMutations辅助函数用来将组件中的 methods 映射为 store.commit 调用
import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`

      // `mapMutations` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
    ]),
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
}

Actions

可以包含异步操作,通过提交mutation来更改store,通过store.dispatch('action-name', payload)来触发,也可以用对象参数

store.dispatch({
  type: 'action-name',
  payload: {...}
})

mapActions辅助函数将组件的 methods 映射为 store.dispatch 调用

import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`

      // `mapActions` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
    ]),
    ...mapActions({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
    })
  }
}
  • dispatch 返回 Promise

Modules

Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
  • 对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象
  • 对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState
  • 对于模块内部的 getter,根节点状态会作为第三个参数暴露出来
  • 进阶

全屏H5时兼容刘海屏

image

问题

  • 假如 H5 页面在 iOS 中占据全屏页面,在这种情况下,除了要兼顾底部小黑条,还要处理页面上部跟状态栏接触的部分,避免内容出现在状态栏上。

方法一(适合 IOS)

    1. 加上 meta
<meta name="viewport" content="(other opts ,)viewport-fit=cover">
    1. 加上 padding
body {
  padding:/* 兼容 iOS < 11.2 */
    constant(safe-area-inset-top)/* 顶部状态栏、底部小黑条(eg: iPhoneX) */
    constant(safe-area-inset-bottom)
    constant(safe-area-inset-right)/* 横屏时的左右不规则屏、小黑条 */
    constant(safe-area-inset-left); 
  padding:/* 兼容 iOS >= 11.2 */
    env(safe-area-inset-top)
    env(safe-area-inset-bottom)
    env(safe-area-inset-right)
    env(safe-area-inset-left); 
}
    1. 针对 fixed 定位的元素,上面的第二步无效,可以在该元素上通过增加 bottom/top/left/right 来实现:
.element {
  bottom: calc(@element-height + constant(safe-area-inset-bottom));
  ...
}

方法二(都适合 👍

需要 APP 端提供能力(JSBridge),前端根据动态获取状态栏的高度来设置样式

参考

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.