26.vue源码分析
1.用到的js知识
1)伪数组如何转成真数组
const list2 = [...list1]
const list3 = Array.from(list1)
const list4 = Array.prototype.slice.call(list1) //改变数组方法的调用者,并将伪数组的元素全部截取并返回
!! const list5 = [].slice.call(list1)
2)节点类型
console.log(eleNode.nodeType) // 1 标签
console.log(attrNode.nodeType) // 2 属性
console.log(txtNode.nodeType) // 3 文本
3) Object.definePrototype()方法---为对象添加属性并配置属性特性
const person = {
firstName: '东方',
lastName: '华哥'
}
Object.defineProperty(person, 'fullName', {
configurable: true, // 默认是false,不能被重设设置,是否可以被删除
enumerable: false, // 默认是false,是否可以枚举
// value:'哈_哈', // 该属性的默认值
// writable:false , // 默认是false, 是否可以被重写
!! get() {// 要使用get()和set()就不能设置默认值和writable
// console.log(person.fullName) 就会进入到get
return this.firstName + '_' + this.lastName
},
set(val) {
// 什么时候会进入到set方法: person.fullName='西门_华哥'
const name = val.split('_')
this.firstName = name[0]
this.lastName = name[1]
}
})
4) Object.keys()方法:将对象的key取出来放在数组中并返回
const keys = Object.keys(person)
5) 对象.hasOwnProperty()方法,判断当前对象中是否包含这个私有属性
console.log(person.hasOwnProperty('fullName')) // true
console.log(person.hasOwnProperty('toString')) // false
6) 文档碎片对象模型----DoucmentFragment---(高效的批量处理多个节点)
// 要求.通过文档碎片对象,把html标签容器中的内容更新
// 1. 创建文档碎片对象
var fragment = document.createDocumentFragment()
// 2. 获取容器对象
var divObj = document.getElementById('demo')
// 3. 把容器对象中的所有的节点全部的存放在文档碎片对象中
var child
while (child = divObj.firstChild) { // 相当于剪切操作
fragment.appendChild(child)
}
// 4. 遍历文档碎片对象中的节点,进行内容更新
fragment.childNodes.forEach(node=>{
node.innerHTML='我才是最帅的'
})
// 5. 把文档碎片对象放在容器中即可
divObj.appendChild(fragment)
2.vue源码文件
1.mvvm.js文件
1)MVVM:创建vue实例的构造函数
1.调用方法实现数据代理:调用Object.keys()/原型上的_proxyData()方法将data对象的属性交给vue实例来代理
2.调用方法实现数据劫持:模板解析前调用observer.js文件中的observe()方法劫持data对象的属性
3.调用方法实现模板解析:调用原型上的$compile()方法解析模板
2)MVVM.prototype原型
1._proxyData():通过Object.defineProperty()方法把data对象中的每个属性添加到Vue的实例对象上,,并重写了Object.defineProperty()方法的set和get方法
2.observer.js文件
1)observe函数:判断传递过来的配置对象的data属性有没有值,或者是不是对象,创建Observer实例对象
2)Observer构造函数
1.把data对象的属性添加到Observer实例对象上
2.调用原型的wall()方法,开始劫持数据,创建dep对象,并且当Dep.target中如果有值,就创建dep对象和Watcher实例对象的依赖关系
3)Observer.prototype原型
1.walk()方法:遍历data对象的属性的key
2.convert()方法:内部调用劫持数据的方法
3.defineReactive()方法
1)调用Dep构造函数创建dep实例对象,每个dep实例对象都有唯一的id标识和一个专门用来保存watcher实例对象的空数组subs
2)实现劫持数据,并重写了Object.defineProperty()方法的set和get方法
4)Dep构造函数:
1.创建dep实例对象,每个dep实例对象都有唯一的id标识,和一个专门用来保存watcher实例对象的空数组subs
2.有一个target属性专门用来储存Watcher的实例对象,来建立dep对象和Watcher实例对象的依赖关系
5)Dep.prototype原型
1.depend():建立dep对象和Watcher实例对象的依赖关系
2.addSub():建立dep对象和Watcher实例对象的依赖关系
3.notify(): data对象属性发生变化通知watcher对象更新数据
3.watcher.js文件
1)Watcher构造函数:将表达式的值添加到watcher实例对象上
2)Watcher.prototype原型
1.parseGetter():返回获取表达式的值的函数
2.get():调用获取表达式的值的函数来获取表达式的值
3.addDep():建立dep对象和Watcher实例对象的依赖关系(把dep的id和dep对象以键值对的方式添加到watcher对象的depIds对象中)
4.update(): data对象属性发生变化,watcher对象更新数据操作
4.compile.js文件
1)Compile编译对象构造函数:
1.创建文档碎片对象,并把容器对象中所有的节点全都存放在文档碎片对象
2.模版解析
3.把模版解析后的文档碎片对象放在容器对象中
2)Compile.prototype原型
1.node2Fragment():创建文档碎片对象,并把容器对象中所有的节点全都存放在文档碎片对象,返回文档碎片对象
2.init():内部调用真正解析模板的方法
3.compileElement():真正解析模板的方法
4.isElementNode():判断当前的节点是不是标签
5.compile():判断标签属性是否是指令,指令是事件指令还是普通指令
6.isDirective():判断是否是指令
7.isEventDirective():判断当前的指令是不是事件指令
8.isTextNode():判断当前的节点是不是文本
9.compileText():调用compileUtil方法来解析文本
3)compileUtil工具对象
1.text属性:执行v-text指令的准备工作,内部会调用v-bind指令
2.html属性:执行v-html指令的准备工作,内部会调用v-bind指令
3.class属性:执行v-class指令的准备工作,内部会调用v-bind指令
4.model属性:执行v-model指令的准备工作,内部会调用v-bind指令
5.bind属性:获取表达式的值,创建Watcher实例对象
6.eventHandler属性:给标签节点添加事件
7._getVMVal:获取表达式的值,展示在页面上
4)updater更新对象
1.textUpdater属性:执行v-text指令
2.htmlUpdater属性:执行v-html指令
3.classUpdater属性:执行v-class指令
4.modelUpdater属性:执行v-model指令
3.剖析vue功能
1)数据代理: 某个对象的属性,可以通过其他对象来访问,在Vue中是有数据代理,Vue的实例对象代理了data对象的属性
创建Vue的实例对象的时候,把data对象中所有数据通过Object.keys()进行遍历,内部调用Object.defineProperty()把data对象中所有的数据一个一个添加到vm实例对象上,
vm对象可以直接访问data对象中的数据了,vm代理了data,data是被代理者
2)数据劫持
当Vue中数据代理结束后,就开始数据劫持,通过observe()方法开始进行数据的劫持,判断data是一个对象后,创建劫持的实例对象,内部遍历vm的data对象,然后把vm的data对象中所有的数据一个一个添加到劫持对象的data对象上,当前在正式添加之前,创建dep对象(id,subs数组),
只要vm中的data对象有多少个属性就会创建多少个dep对象(将来和watcher建立关系)
3)模板解析:把页面中html模版里面使用到的表达式(插值语法/事件指令/一般指令),解析为真正的数据的操作,并渲染界面
1)在创建Vue实例对象时候,数据代理和数据劫持后,开始模版解析,会在MVVM的对象中实例化Compile对象
2)Compile内部会把当前的Vue实例对象控制的容器对象中所有的节点全都存放在文档碎片对象中(文档碎片对象可以高效的批量操作DOM节点,在内存中进行节点的操作,这是所谓的虚拟DOM)
3)取出文档碎片对象所有的子节点进行遍历,如果是文本节点,并且符合插值语法的正则,就要调用CompileUtil对象中的相关方法进而调用bind方法,
然后在调用updater对象中相关的方法把当前节点用到的表达式进行数据的替换,最后渲染页面即可
4)取出文档碎片对象所有的子节点进行遍历,如果当前的节点是标签节点,取出当前标签节点的所有属性,遍历所有属性
然后判断每个属性是不是Vue中的指令(以v-开头),然后判断当前的指令是事件指令(v-后面是:on)还是一般指令
5)如果是事件指令,就把当前这个指令进行字符串切割,获取事件类型,还要拿着事件回调函数名去vue实例对象的methods属性中找对应的回调函数,
然后通过addEventListener方法为当前的标签节点绑定对应的事件,并将事件回调函数的this指向vue实例对象,最后在通过removeAttribute()移除当前标签节点的所有属性,最后渲染页面
6)如果是普通指令,调用CompileUtil中的相关方法,进而调用bind方法,然后在调用updater对象中相关的方法把当前节点用到的表达式进行数据的替换,最后渲染页面即可
1.updater对象中如何执行v-text和v-html指令?
通过文本节点的textContent和innerHTML属性替换表达式值
2.updater对象中如何执行v-class指令?
获取当前标签节点的类样式的名字,如果有类名就添加一个空格在拼接上表达式的值,在把最终值添加给当前标签节点的类样式
7)模板解析中,当在内存中成功替换表达式的值之后,bind方法内部会创建watcher对象,会根据表达式的个数来创建对应个数的watcher对象
进入到watcher内部后,会调用get方法,进而完成开始建立dep对象和watcher对象的关系,进来会监视data对象属性的变化
dep和watcher的关系类型:
1对1的关系:1个dep对应一个watcher,data中只有一个属性,模版中只用了一个表达式
1对多的关系:1个dep对应多个watcher,data中只有一个属性,模版中用了多个表达式
多对1的关系:多个dep对应1个watcher,data中有多个属性,模版中用了一个表达式(data属性是对象,表达式:对象.属性)
多对多的关系:多个dep对应多个watcher,data中有多个属性,模版中用了多个表达式(data属性是对象,表达式:对象.属性)
4)双向数据绑定
创建Vue的实例的时候,除了数据代理和数据劫持以外,会进入到compile模版解析中,在内存中创建文档碎片对象,把html容器中所有的子级节点全部的存放在文档碎片对象中,
遍历所有的节点判断当前的节点是不是标签,然后获取当前节点标签中所有的属性,判断当前的属性是不是指令,然后再判断当前的指令是不是普通指令v-model,是的话就把把表达式的值赋值给节点value属性,再然后为当前的节点标签绑定input事件
如果标签中的数据发生变化,此时触发input事件,判断表达式之前的数据和现在输入的数据是否不同,之后会进入到MVVM的set方法内部再进入到observer.js的set方法内部,
根据当前的这个属性的dep对象通知当前dep对象中subs数组中的watcher进行数据的更新操作
(vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的)
27.vue-property-decorator(简化ts在vue中的写法)
vue-property-decorator是在Vue中使用TypeScript时,非常好用的一个库,使用装饰器来简化书写。(装饰器的作用就是接收vue组件,返回处理过后的vue组件)
该库完全依赖于vue-class-component,因此在使用该库之前,请先阅读vue-class-component库。
1)@component装饰器(来源于vue-class-component)
@Component({
name:'',
components:{},
filters:{},
directives:{}
})
@Component装饰器可以接收一个对象作为参数,可以在对象中声明name , components ,mixins,filters,directives等装饰器选项,
也可以声明computed,watch等,但并不推荐这么做,因为在访问this时,编译器会给出错误提示
// 为了使用TypeScript,需要在script标签上添加 lang = ts
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import User from '@/components/User.vue'
// 还可添加directives字段
@Component({
name: 'HelloWorld', // name属性可以省略,效果是一样的,因为class HelloWorld extends Vue实现了vue组件名为HelloWorld
components: {
User
}
})
export default class HelloWorld extends Vue {
}
</script>
// 等价于
<script>
import User from '@/components/User.vue'
export default {
name: 'HelloWorld',
components: {
User
}
})
</script>
2)@Prop, Data, methods, computed, watch
1.使用props
//前面类型大写,后面可以小写
@Prop(options: (PropOptions | Constructor[] | Constructor) = {})
@Prop(String)
@Prop([String,Number])
@Prop({type: String, default: 'Developer',required: false})
@Prop装饰器接收一个参数,这个参数可以有三种写法(字符串、数组、对象),可以添加 type, default, required为props指定验证要求,同样也可以使用 readonly 禁止操作
<script>
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class HelloWorld extends Vue {
//属性的ts类型后面需要加上undefined类型;或者在属性名后面加上!,表示非null 和 非undefined 的断言,否则编译器会给出错误提示
//@Prop()限制的是其它组件传递过来的属性,propA!: string限制的是组件内接收的属性,一般@Prop()和propA!: string的限制一样
@Prop() propA!: string
@Prop(String) propB:string|undefined;
@Prop([String,Number]) propC!:string|number;
@Prop({type: String, default: 'Developer',required: false}) readonly propD!: string
}
</script>
// 等价于
export default {
props: {
propA,
propB:String,
propC:[String,Number],
propD: {
type: string,
default: 'Developer',
required: false
}
}
}
2.使用data(data数据可以声明为类属性)
export default class HelloWorld extends Vue {
private msg: string = "welcome to my app"
private list: Array<object> = [
{
name: 'Melody',
age: '20'
},
{
name: 'James',
age: '20'
}
]
}
//等价于
export default {
data() {
return {
msg: "welcome to my app",
list: [
{
name: 'Melody',
age: '20'
},
{
name: 'James',
age: '20'
}
]
}
}
3.使用Computed 属性(computed属性可以声明为类属性访问器)
export default class HelloWorld extends Vue {
get fullName(): string {
return this.first+ ' '+ this.last
}
}
// 等价于
export default {
computed:{
fullName() {
return this.first + ' ' + this.last
}
}
}
当需要写一个稍微复杂点的涉及到setter和getter的 computed属性时,在ts中写法如下
export default class HelloWorld extends Vue {
get fullName(): string {
return this.first+ ' '+ this.last
}
set fullName(newValue: string) {
let names = newValue.split(' ')
this.first = names[0]
this.last = names[names.length - 1]
}
}
//等价于
export default {
computed:{
fullName: {
get: function () {
return this.first + ' ' + this.last
},
set: function (newValue) {
let names = newValue.split(' ')
this.first = names[0]
this.last = names[names.length - 1]
}
}
}
}
4.使用watch属性
@Watch(path: string, options: WatchOptions = {})
@Watch 装饰器接收两个参数
path:被侦听的属性名
options:可以包含两个属性
immediate:侦听开始之后是否立即调用该回调函数
deep:是否开启深度监视
侦听开始,发生在beforeCreate勾子之后,created勾子之前
@Watch('child')
onChildChanged (val: string, oldVal: string) {
if (val !== oldVal) {
window.console.log(val)
}
}
//等价于
watch: {
'child': {
handler: 'onChildChanged',
immediate: false,
deep: false
}
},
method: {
onChildChanged(val, oldVal) {
if (val !== oldVal) {
window.console.log(val)
}
}
}
5.使用method属性(methods可以直接声明为类成员方法)
export default class HelloWorld extends Vue {
public clickMe(): void {
console.log('clicked')
console.log(this.addNum(4, 2))
}
public addNum(num1: number, num2: number): number {
return num1 + num2
}
}
//等价于
export default {
methods: {
clickMe() {
console.log('clicked')
console.log(this.addNum(4, 2))
}
addNum(num1, num2) {
return num1 + num2
}
}
}
6.Lifecycle hooks(生命周期函数)
所有Vue生命周期挂钩也可以直接声明为类成员方法
export default class HelloWorld extends Vue {
mounted() {}
beforeUpdate() {}
}
//等价于
export default {
mounted() {}
beforeUpdate() {}
}
3)@Emit 装饰器
子组件触发父组件的自定义事件并传递数据,在TypeScript中使用@Emit 装饰器
@Emit(event?: string)
@Emit 装饰器接收一个可选参数,该参数是$Emit的第一个参数,充当事件名。如果没有提供这个参数,$Emit会将回调函数名的camelCase转为kebab-case,并将其作为事件名
@Emit会将回调函数的返回值作为第二个参数,如果返回值是一个Promise对象,$emit会在Promise对象被标记为resolved之后触发,promise的结果值作为第二个参数
@Emit的回调函数的参数,会放在其返回值之后,一起被$emit当做参数使用
1.@Emit()没有指定参数,相当于触发'add-to-count',addToCount函数参数为传递给'add-to-count'的数据
import { Vue, Component, Emit } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@Emit()
addToCount(n: number) {
this.count += n
}
}
//等价于
export default {
methods: {
addToCount(n) {
this.count += n
this.$emit('add-to-count', n)
}
}
}
2.@Emit()没有指定参数,相当于触发'return-value',returnValue函数返回值为传递给'return-value'的数据
@Emit()
returnValue() {
return 10
}
returnValue() {
this.$emit('return-value', 10)
}
3.@Emit()没有指定参数,相当于触发'on-input-change',onInputChange函数返回值和参数为传递给'return-value'的数据
@Emit()
onInputChange(e) {
return e.target.value
}
onInputChange(e) {
this.$emit('on-input-change', e.target.value, e)
}
4.@Emit()没有参数并且返回一个Promise对象,Promise对象的结果值为传递给'promise'的数据
@Emit()
promise() {
return new Promise(resolve => {
resolve(20)
})
}
//等价于
promise() {
const promise = new Promise(resolve => {
resolve(20)
})
promise.then(value => {
this.$emit('promise', value)
})
}
5.@Emit()有参数,那么触发'reset'
@Emit('reset')
resetCount() {
this.count = 0
}
//等价于
resetCount() {
this.count = 0
this.$emit('reset')
}
4)vuex
1.首先安装
npm install vuex-module-decorators -D
npm install vuex-class -D
2.如果想通过名字空间的形式来使用module, 需在@Module装饰器中添加额外的参数.
例如, 以下示例代码中添加一个namespaced为user的module
import { VuexModule, Module, Mutation, Action } from 'vuex-module-decorators'
@Module({ namespaced: true, name: 'user' })
class User extends VuexModule {
public name: string = ''
@Mutation
public setName(newName: string): void {
this.name = newName
}
@Action
public updateName(newName: string): void {
this.context.commit('setName', newName)
}
}
export default User
注意:@Module装饰器的属性字段name值, 必须与new store({modules: {}})中注册module name名称保持一致
//等价于
export default {
namespaced: true,
state: {
name: ''
},
mutations: {
setName(state, newName) {
state.name = newName
}
},
actions: {
updateName(context, newName) {
context.commit('setName', newName)
}
}
}
3.在组件里面使用vuex
要使用Vuex,可以使用vuex-class库。该库提供了装饰器,可以在Vue组件中绑定State,Getter,Mutation和Action。
由于已经使用了命名空间的Vuex模块,因此我们首先从vuex-class导入命名空间,然后传递模块名称以访问该模块
import { Component, Vue } from 'vue-property-decorator'
import { namespace } from 'vuex-class'
const user = namespace('user')
@Component
export default class User extends Vue {
@user.State
public name!: string
@user.Getter
public nameUpperCase!: string
@user.Action
public updateName!: (newName: string) => void
}
//等价于
import { mapState, mapGetters, mapActions} from 'vuex'
export default {
computed: {
...mapState('user', ['name']),
...mapGetters('user', ['nameUpperCase'])
}
methods: {
...mapActions('user', ['updateName'])
}
}
5.其它
1)@Provide 提供 / @Inject 注入
与 React的context组件间通信相似,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代传递数据,
数据通过Provide传递下去,然后子组件通过Inject来获取
import { Component, Inject, Provide, Vue } from 'vue-property-decorator'
const symbol = Symbol('baz')
@Component
export class MyComponent extends Vue {
@Inject() foo!: string
@Inject('bar') bar!: string
@Inject({ from: 'optional', default: 'default' }) optional!: string
@Inject(symbol) readonly baz!: string
@Provide() foo = 'foo'
@Provide('bar') baz = 'bar'
}
//等价于
const symbol = Symbol('baz')
export const MyComponent = Vue.extend({
inject: {
foo: 'foo',
bar: 'bar',
optional: { from: 'optional', default: 'default' },
baz: symbol,
},
data() {
return {
foo: 'foo',
baz: 'bar',
}
},
provide() {
return {
foo: this.foo,
bar: this.baz,
}
},
})
2)@Ref(refKey?: string)
@Ref 装饰器接收一个可选参数,用来指向元素或子组件的引用信息。如果没有提供这个参数,会使用装饰器后面的属性名充当参数
import { Vue, Component, Ref } from 'vue-property-decorator'
import { Form } from 'element-ui'
@Componentexport default class MyComponent extends Vue {
@Ref() readonly loginForm!: Form
@Ref('changePasswordForm') readonly passwordForm!: Form
public handleLogin() {
this.loginForm.validate(valide => {
if (valide) {
// login...
} else {
// error tips
}
})
}
}
//等价于
export default {
computed: {
loginForm: {
cache: false,
get() {
return this.$refs.loginForm
}
},
passwordForm: {
cache: false,
get() {
return this.$refs.changePasswordForm
}
}
}
}
3)@Model装饰器允许我们在一个组件上自定义v-model
@Model(event?: string, options: (PropOptions | Constructor[] | Constructor) = {})
event:事件名。
options:与@Prop的第一个参数一致,可以是字符串、数组、对象类型
下面例子中指定的是change事件,所以我们还需要在template中加上相应的事件:
<template>
<input
type="text"
:value="value"
@change="$emit('change', $event.target.value)"
/>
</template>
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,
},
},
}
4)Mixins
假设当前已经有一个mixins/ProjectMixin文件 如何在其他组件里面使用方式如下
import { Component, Vue, Mixins } from 'vue-property-decorator'
import ProjectMixin from '@/mixins/ProjectMixin'
@Component
export default class Project extends Mixins(ProjectMixin) {
get projectDetail(): string {
return this.projName + ' ' + 'HS'
}
}
//等价于
import ProjectMixin from '@/mixins/ProjectMixin'
export default {
mixins: [ ProjectMixin ],
computed: {
projectDetail() {
return this.projName + ' ' + 'HS'
}
}
}
5)@PropSync装饰器与@prop用法类似,二者的区别在于:
@PropSync(propName: string, options: (PropOptions | Constructor[] | Constructor) = {})装饰器接收两个参数:
propName:表示父组件传递过来的属性名
options:与@Prop的第一个参数一致,可以是字符串、数组、对象类型
@PropSync 会生成一个新的计算属性。
import { Vue, Component, PropSync } from 'vue-property-decorator'
@Component
export default class MyComponent extends Vue {
@PropSync('propA', { type: String, default: 'abc' }) public syncedPropA!: string
}
//等价于
export default {
props: {
propA: {
type: String,
default: 'abc'
}
},
computed: {
syncedPropA: {
get() {
return this.propA
},
set(value) {
this.$emit('update:propA', value)
}
}
}
}
注意:@PropSync需要配合父组件的.sync修饰符使用
6.总结
1)methods可以直接声明为类成员方法(使用public关键字)
2)computed属性可以声明为类属性访问器(get/set)
3)data数据可以声明为类属性(使用private关键字)
4)render函数和所有Vue生命周期挂钩也可以直接声明为类成员方法,但不能在实例本身上调用它们。
28.服务器端渲染 (SSR)
将一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。
1)为什么使用服务器端渲染
1.更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。
2.更快的内容到达时间,无需等待所有的 JavaScript都完成下载并执行,才显示服务器渲染的标记,所以你的用户将会更快速地看到完整渲染的页面。
3.服务端渲染, 解决首屏加载速度, 和 seo问题(总结)
2)服务端渲染实例
// 第 1 步:创建一个 Vue 实例
const Vue = require('vue')
const app = new Vue({
template: `<div>{{myname}}-{{myage}}</div>`,
data:{
myname:"kerwin",
myage:100
}
})
// 第 2 步:创建一个 renderer
const renderer = require('vue-server-renderer').createRenderer()
// 第 3 步:将 Vue 实例渲染为 HTML
// 旧写法
// renderer.renderToString(app, (err, html) => {
// if (err) throw err
// console.log(html)
// // => <div data-server-rendered="true">Hello World</div>
// })
// 新写法,在 2.5.0+,如果没有传入回调函数,则会返回 Promise:
renderer.renderToString(app).then(html => {
console.log(html)
}).catch(err => {
console.error(err)
})
3)Nuxt.js
Nuxt.js是一个做vue ssr(服务端渲染)的框架
Nuxt.js工作流:
Incoming Request(客户端发送请求给客户端)-->nuxtServerLint(store action)(服务端检测有没有nuxtServerLint这个配置,有的话就执行这个函数用来操作vuex)
-->middleware(操作路由相关的功能)-->validate(路由校验)-->asyncData(vue组件数据)和fetch(vuex数据)-->render(将数据渲染到页面)-->navigate(nuxt link)(组件中调用nuxt link重新从middleware开始循环)
npm i -g create-nuxt-app
create-nuxt-app 项目名
//下面这个命令等价于上面两个命令,npx会检测全局和局部有没有create-nuxt-app命令,有的话就直接创建项目,没有的
//话就先在本地下载create-nuxt-app命令,再去创建项目
npx create-nuxt-app 项目名
1.项目结构
.nuxt
assets 放静态资源
components 放全局公共组件
layouts 布局组件模板
middleware 中间件做路由拦截
pages 放路由组件
plugins 放插件,如elememt-ui
server 服务端配置
static 放静态资源,小图标等
store 放vuex
nuxt.config.js(nuxt配置)
2.路由
1)Nuxt.js 依据 pages 目录结构自动生成 vue-router 模块的路由配置
2)路口文件(layouts/default.vue)
3)<nuxt-link>等价于<router-link/>,支持activeClass(通过activeClass给标签加一个类名,在样式中写触发时高亮样式),tag
<nuxt/>等价于<router-view>
4)嵌套路由
1.在pages中创建一个与一级路由相同文件名的文件夹来放该路由的子路由
2.在父组件中通过<nuxt-child>标签来显示子路由
5)路由重定向
1.配置nuxt.config.js文件
router:{
extendRoutes(routes){
routes.push({
path:"/",
redirect:'/film/nowplaying'
})
}
}
2.利用中间件来处理
//middleware/redirect.js(名字可改)文件中
export default function({ isHMR,app,store,route,params,error,redirect }) {
if (isHMR) return
if(route.fullPath == '/film') {
return redirect('/film/nowplaying')
}
}
//配置nuxt.config.js文件
router: {
middleware: 'redirect' // 即每次路由跳转会调用该中间件 ,redirect是文件名
//middleware: ['redirect'] //多个中间件写法
}
6)动态路由
1.创建
在pages创建路由文件夹,文件夹里面以下划线开头,下划线后面是路由参数
pages
‐‐| detail(文件夹,不需要在外面创建.vue文件)
‐‐‐‐‐| _id.vue
2.获取路由参数
this.$router.query.id
7)视图
在layout 里面 写好default.vue 可以认为这是根组件的模板了, 所有的组件都加在里面,但是有些页面 可能不一样,就可以使用 个性化定制页面。
export default {
layout: 'template'
}
8)服务端渲染异步数据(ssr)
服务端渲染的异步数据可以通过右键查源码查到数据,而客户端渲染的异步数据则不可以
data() {
return {
list: []
}
},
// 在asyncData()方法中实现服务端渲染异步数据,return回来的list会给data的list
// 在当前页面刷新, 服务端执行此函数,从其他页面跳转过来,客户端执行此函数
// data中可以拿到route、query、params等数据
// 只有页面级别组件才有
async asyncData(data) {
let {status, data: {list}} = await axios.get('http://localhost:3000/city/list')
if (status === 200) {
return {
list
}
}
}
或者
asyncData(data) {
return axios({
url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=6341699
}).then(res=>{
return {
datalist:res.data.data.films
}// 状态
})
}
9)反向代理解决跨域
1.npm i @nuxtjs/proxy ‐D
2.在 nuxt.config.js 配置文件中添加对应的模块,并设置代理
modules: [
'@nuxtjs/axios',
'@nuxtjs/proxy'
],
axios: {
proxy:true
},
proxy:{
//拦截/ajax开头的请求,拼接到http://m.maoyan.com上
'/ajax':{
target:"http://m.maoyan.com",
changeOrigin:true
}
},
3.在组件中,上面代理只能解决前端跨域问题,但是如果asyncData函数在后端执行会出现请求路径不全问题
asyncData(data) {
return axios({
//process.server为true说明asyncData函数在服务端执行
url:process.server?"http://m.maoyan.com/ajax/movieOnInfoList?token=":"/ajax/movieOnInfoList?token=",
}).then(res=>{
console.log(res.data);
return {
datalist:res.data.movieList
}// 状态
})
}
10)vuex在nuxt.js中的使用
直接在项目中创建store文件夹,配置store,nuxt.js会自动引入store,我们可以直接在组件中使用
const store = () => new Vuex.Store({
modules: {
city,
navbar
},
// 注意store中actions不能省略,nuxt.js会执行nuxtServerInit()方法实现vuex数据的服务端渲染
actions: {
// nuxtServerInit({ commit }, { req }) {
// if (req.session.user) {
// commit('city', req.session.user)
// }
// }
}
})
29性能优化
1)开发过程
1.优先使用vIf
v-if是将元素删除来达到隐藏的效果,v-show是将元素display:none来达到隐藏的效果,如果需要频繁切换才使用v-show
2.vFor key避免使用index作为标识
当index作为标识的时候,插入一条数据的时候,列表中它后面的key都发生了变化,那么当前的 vFor 都会对key变化的 Element 重新渲染,
但是其实它们除了插入的 Element 数据都没有发生改变,这就导致了没有必要的开销。
3.释放组件资源
每创建出一个事物都需要消耗资源,资源不是凭空产生的,是分配出来的。所以说,当组件销毁后,尽量把我们开辟出来的资源块给销毁掉,
比如 setInterval , addEventListener等
4.长列表
长列表渲染的时候,建议将DOM移除掉,类似于图片懒加载的模式,只有出现在视图上的DOM才是重要的DOM。网络上有一些很好的解决方案,如 vue-virtual-scroller 库等等
5.图片合理的优化方式
图片应该都不陌生吧,在网页中,往往存在大量的图片资源,这些资源或大或小。当我们页面中DOM中存在大量的图片时,
难免不会碰到一些加载缓慢的问题,导致图片出现加载失败的问题。网络上大部分都在使用 懒加载 的使用方式,
只有当 存在图片的DOM 出现在页面上才会进行图片的加载,无形中起到了分流的作用,下面就说一套实践的方案吧
1)小图标使用 SVG 或者字体图标
2)通过 base64 和 webp 的方式加载小型图片
3)能通过cdn加速的大图尽量用cdn
4)大部分框架都带有懒加载的图片
6.使用路由懒加载
component: () => import('@/components/HelloWorld')
7.第三方模块(UI/工具插件)按需导入
8.SPA 页面采用keep-alive缓存组件
9.防抖、节流
10.首屏优化
众所周知,第一次打开Vue的时候,如果你的项目够大,那么首次加载资源时,会非常的久。由于资源没有加载完毕,
界面的DOM也不会渲染,会造成白屏的问题。用户此时并不知道是加载的问题,所以会带来一个不好的体验。
因此通常会在public下写一个加载动画,告诉用户,网页在加载中这个提示。当页面加载成功后,页面渲染出来的这一个体验比白屏等开机要好太多了。
11.静态模板尽量使用函数组件替代普通组件
12.render函数只要数据一改变就会触发,所以数据的处理尽量放在render函数外面,render函数里面的方法提前定义在外面
因为方法是执行render函数临时创建的。消耗性能
13.结构显示隐藏加动画
2)webpack
1.优化打包构建速度
1) HMR 热模块替换
2) cache 缓存(针对js)
3) oneOf
4) 多进程打包
2.优化打包代码体积和性能
1) 兼容性处理
2) tree shaking 树摇
3) code split 代码分割 / lazy loading 懒加载
4) preload 和 prefetch 预加载
5) cache 缓存(浏览器缓存)
6) PWA 渐进式网络应用程序(离线加载技术)
3)其它
1.服务端渲染SSR
1)vuex中
actions: {
//app相当于实例对象
nuxtServerInit({commit}, {req, app}) {
}
}
2)组件内
//ctx相当于实例对象
asyncData(ctx){}
2.还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。
//熊健静态服务器/utils/compress.js文件
const { createGzip, createDeflate } = require("zlib");
// 设置响应头,告诉客户端,内容经过了压缩
res.setHeader("Content-Encoding", "gzip");
// rs可读流会将数据读取传给gzip压缩,返回值还是一个rs(可读流中的数据已经压缩了~)
rs = rs.pipe(createGzip());
3.依赖库CDN加速
看到有小伙伴使用CDN的方式引入一些依赖包,觉得非常的 Nice ,然后我也开始使用了。我将 Vue Axios Echarts 等等都分离了出来,
在正式环境下,通过CDN,确实有了一些明显的提升,所以说大家可以进行尝试。
// 在html引入script标签后。在vue的配置中,进行声明
configureWebpack: {
externals: {
'echarts': 'echarts' // 配置使用CDN
}
}
30.项目结构分析
gshop(脚手架2)
|-- build : webpack 相关的配置文件夹(基本不需要修改)
|-- config: webpack 相关的配置文件夹(基本不需要修改)
|-- index.js: 指定后台服务的端口号和静态资源文件夹
|-- node_modules
|-- src
|-- components------------非路由组件文件夹
|-- FooterGuide---------------底部组件文件夹
|-- FooterGuide.vue--------底部组件 vue |--
pages-----------------路由组件文件夹
|-- Msite---------------首页组件文件夹
|-- Msite.vue--------首页组件 vue
|-- Search----------------搜索组件文件夹
|-- Search.vue---------搜索组件 vue
|-- Order--------------订单组件文件夹
|-- Order.vue-------订单组件 vue
|-- Profile--------------个人组件文件夹
|-- Profile.vue-------个人组件 vue |--
App.vue---------------应用根组件 vue
|-- main.js---------------应用入口 js
|-- static: 静态资源文件夹
|-- .babelrc: babel 的配置文件
|-- .editorconfig: 通过编辑器的编码/格式进行一定的配置
|-- .eslintignore: eslint 检查忽略的配置
|-- .eslintrc.js: eslint 检查的配置
|-- .gitignore: git 版本管制忽略的配置
|-- index.html: 主页面文件
|-- package.json: 应用包配置文件
|-- README.md: 应用描述说明的 readme 文件
shop-client(脚手架3)
|-- node_modules
|-- public: 任何放置在 public 文件夹的静态资源都会被简单的复制,而不经过 webpack。你需要通过绝对路径来引用它们。
|-- index.html: 主页面文件
|-- src
|-- main.js: 应用入口js
|-- babel.config.js: babel的配置文件
|-- vue.config.js: vue的配置文件
|-- .gitignore: git版本管制忽略的配置
|-- package.json: 应用包配置文件
|-- README.md: 应用描述说明的readme文件
31.vue.config.js文件配置
const isProduction = process.env.NODE_ENV === 'production';
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const cdn = {
css: ["https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.8.2/theme-chalk/index.css"],
js: [
'https://cdn.bootcss.com/vue/2.5.17/vue.runtime.min.js',
'https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js',
'https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js',
'https://cdn.bootcss.com/axios/0.18.0/axios.min.js',
'https://cdn.bootcss.com/element-ui/2.8.2/index.js',
'https://cdn.bootcss.com/echarts/3.8.5/echarts.min.js'
]
}
module.exports = {
// 基本路径
// '/'绝对路径,'./'或''相对路径
publicPath: process.env.NODE_ENV === 'production' ? '/' : './',
// 运行 vue-cli-service build 时生成的生产环境构建文件的目录
// 默认构建前清除文件夹(构建时传入 --no-clean 可关闭该行为
outputDir: 'dist',
// 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录
assetsDir: 'static',
// 指定生成的 index.html 的输出路径 (相对于 outputDir),也可以是一个绝对路径
indexPath: 'index.html',
// 生成的静态资源在它们的文件名中包含了 hash 以便更好的控制缓存
filenameHashing: true,
// 当在 multi-page 模式下构建时,webpack 配置会包含不一样的插件
// (这时会存在多个 html-webpack-plugin 和 preload-webpack-plugin 的实例)。
// 如果你试图修改这些插件的选项,请确认运行 vue inspect
pages: {
index: {
// page 的入口
entry: 'src/pages/index/index.js',
// 模板来源
template: 'src/pages/index/index.html',
// 在 dist 的输出为 index.html
filename: 'index.html',
// 当使用 title 选项时,
// template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
title: '首页',
// 在这个页面中包含的块,默认情况下会包含
// 提取出来的通用 chunk 和 vendor chunk。
chunks: ['chunk-vendors', 'chunk-common', 'index']
},
// 当使用只有入口的字符串格式时,
// 模板会被推导为 `public/subpage.html`
// 并且如果找不到的话,就回退到 `public/index.html`。
// 输出文件名会被推导为 `subpage.html`。
// 多入口时,接着写子页面
//subpage: 'src/subpage/main.js'
},
// eslint-loader 是否在保存的时候检查
lintOnSave: true,
// 是否使用包含运行时编译器的Vue核心的构建
runtimeCompiler: false,
// 默认情况下 babel-loader 忽略其中的所有文件 node_modules,
// 想要通过 Babel 显式转译一个依赖,可以在这个选项中列出来
transpileDependencies: [],
// 生产环境 sourceMap
productionSourceMap: false,
// 跨域设置
// 可取值参考: https://developer.mozilla.org/zh-CN/docs/Web/HTML/CORS_settings_attributes
crossorigin: undefined,
// 构建后的文件是部署在 CDN 上的,启用该选项可以提供额外的安全性, 默认false
integrity: false,
// webpack 配置,键值对象时会合并配置,为方法时会改写配置
// https://cli.vuejs.org/guide/webpack.html#simple-configuration
//configureWebpack: (config) => {},
// webpack 链接 API,用于生成和修改 webapck 配置
// https://github.com/mozilla-neutrino/webpack-chain
chainWebpack: (config) => {
if (isProduction) {
// 删除预加载
config.plugins.delete('preload');
config.plugins.delete('prefetch');
// 压缩代码
config.optimization.minimize(true);
// 分割代码
config.optimization.splitChunks({
chunks: 'all'
})
// 生产环境注入cdn
config.plugin('html')
.tap(args => {
args[0].cdn = cdn;
return args;
});
}
// 因为是多页面,所以取消 chunks,每个页面只对应一个单独的 JS / CSS
config.optimization
.splitChunks({
cacheGroups: {}
});
// 'src/lib' 目录下为外部库文件,不参与 eslint 检测
config.module
.rule('eslint')
.exclude
.add('/Users/maybexia/Downloads/FE/community_built-in/src/lib')
.end()
},
configureWebpack: config => {
if (isProduction) {
// 用cdn方式引入
config.externals = {
'vue': 'Vue',
'vuex': 'Vuex',
'vue-router': 'VueRouter',
'axios': 'axios',
'element-ui': 'ELEMENT',
'echarts': 'echarts'
}
// 为生产环境修改配置...
config.plugins.push(
//生产环境自动删除console
new UglifyJSPlugin({
uglifyOptions: {
compress: {
warnings: true,
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log']//移除console
}
},
sourceMap: false,
parallel: true
})
);
} else {
// 为开发环境修改配置...
}
},
// 配置高于chainWebpack中关于 css loader 的配置
css: {
// false 时只有 *.module.[ext] 结尾的文件才会被视作 CSS Modules 模块
// true 时可以去掉文件名中的 .module, 并将所有的 *.(css|scss|sass|less|styl(us)?) 文件视为 CSS Modules 模块
modules: false,
// 是否使用 css 分离插件 ExtractTextPlugin,采用独立样式文件载入,不采用 <style> 方式内联至 html 文件中
// 生产环境下是 true,开发环境下是 false
extract: true,
// 是否构建样式地图,设置为 true 之后可能会影响构建的性能
sourceMap: false,
// css预设器配置项
loaderOptions: {
css: {
// 这里的选项会传递给 css-loader
},
postcss: {
// 这里的选项会传递给 postcss-loader
}
}
},
// 所有 webpack-dev-server 的选项都支持
// https://webpack.js.org/configuration/dev-server/
devServer: {
open: true,
host: '127.0.0.1',
port: 3000,
https: false,
hotOnly: false,
// 将任何未知请求 (没有匹配到静态文件的请求) 代理到该字段指向的地方
proxy: null,
// proxy: 'http://123.206.33.109:8081', // 配置跨域处理,只有一个代理
proxy: {
// 配置多个代理y
"/api": {
target: "http://localhost:8088",//这里改成你自己的后端api端口地址,记得每次修改,都需要重新build
//target: "http://localhost:58427",
//target: "http://api.douban.com",
ws: true,
changeOrigin: true,
pathRewrite: {
// 路径重写,
"^/apb": "" // 替换target中的请求地址
}
}
},
before: app => {
}
},
// 构建时开启多进程处理 babel 编译
// 是否为 Babel 或 TypeScript 使用 thread-loader
parallel: require('os').cpus().length > 1,
// PWA 插件相关配置
// https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
pwa: {},
// 第三方插件配置
pluginOptions: {}
};
使用了CDN的方式加载了比较重的js并在index.html头部引入cdn
<--使用CDN的JS文件-->
<% for(var i in htmlWebpackagePlugin.options.cdn&&htmlWebpackagePlugin.options.cdn.js){
%>
<link href="<%=htmlWebpackagePlugin.options.cdn.js[i]%>" rel="preload" as="script">
%>
}<%
<--使用CDN的JCSS文件-->
<% for(var i in htmlWebpackagePlugin.options.cdn&&htmlWebpackagePlugin.options.cdn.css){
%>
<link href="<%=htmlWebpackagePlugin.options.cdn.css[i]%>" rel="preload" as="style">
<link href="<%=htmlWebpackagePlugin.options.cdn.css[i]%>" rel="stylesheet">
%>
}<%
访问发现确实变快了,但是服务器只有2M,我想更快居然可以压缩代码,config配置下 放在configureWebpack里
new CompressionWebpackPlugin({
filename: '[path].gz[query]', // 提示 compression-webpack-plugin@3.0.0的话asset改为filename
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 10240,
minRatio: 0.8,
// 配置参数详解
// 提示 compression-webpack-plugin@3.0.0的话asset改为filename
// asset: 目标资源名称。 [file] 会被替换成原始资源。[path] 会被替换成原始资源的路径, [query] 会被替换成查询字符串。默认值是 "[path].gz[query]"。
// algorithm: 可以是 function(buf, callback) 或者字符串。对于字符串来说依照 zlib 的算法(或者 zopfli 的算法)。默认值是 "gzip"。
// test: 所有匹配该正则的资源都会被处理。默认值是全部资源。
// threshold: 只有大小大于该值的资源会被处理。单位是 bytes。默认值是 0。
// minRatio: 只有压缩率小于这个值的资源才会被处理。默认值是 0.8。
nginx也做压缩的调整就可以更快
# 开启gzip
gzip on;
# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;
# gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
gzip_comp_level 2;
# 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
# 是否在http header中添加Vary: Accept-Encoding,建议开启
gzip_vary on;
32.babel.config.js文件配置
module.exports = {
presets: [
'@vue/app'
],
// 如果需要按需引入第三方库,在下面添加
plugins:[
[
"import",
{
"libraryName": "vant",
"libraryDirectory": "es",
"style": true
}
],
[
"import",
{
"libraryName": "we-vue",
"style": "style.css"
},
"we-vue"
]
]
}
33. .eslintrc.js文件详情
{
root: true, //指定配置文件根目录:表示当前文件为eslint的根配置文件,逐层查找时无需往更上一级的文件目录中进行搜索
parser: 'babel-eslint',//指定eslint解析器:babel-eslint是围绕Babel解析器的包装器使其与ESLint兼容;可能值espree、esprima
parserOptions: { //eslint解析器配置项
sourceType: "module",//指定js的导入方式,module是指通过模块导入,默认值为script(表示通过script标签引入)
},
env: { //运行环境极其局全局变量
browser: true, //浏览器环境
},
plugins: [//提供插件
'html' //插件名称,省略了[eslint-plugin-]前缀,表示规范html
],
extends: 'airbnb-base', //规则继承:airbnb-base包含了JS、Es6的语法检查要依赖于[eslint-pugin-import];另一个值standard表示使用标准的js语法规则
settings: { //添加共享规则参数,会提供给每一个规则,但是规则使不使用,看规则的设置
'import/resolver': {
webpack: { //解析webpack配置项,路径为bulid/webpack.base.conf.js
config: 'bulid/webpack.base.conf.js'
}
}
},
rules:{
"no-alert": 0,//禁止使用alert confirm prompt
"no-array-constructor": 2,//禁止使用数组构造器
"no-bitwise": 0,//禁止使用按位运算符
"no-caller": 1,//禁止使用arguments.caller或arguments.callee
"no-catch-shadow": 2,//禁止catch子句参数与外部作用域变量同名
"no-class-assign": 2,//禁止给类赋值
"no-cond-assign": 2,//禁止在条件表达式中使用赋值语句
"no-console": 2,//禁止使用console
"no-const-assign": 2,//禁止修改const声明的变量
"no-constant-condition": 2,//禁止在条件中使用常量表达式 if(true) if(1)
"no-continue": 0,//禁止使用continue
"no-control-regex": 2,//禁止在正则表达式中使用控制字符
"no-debugger": 2,//禁止使用debugger
"no-delete-var": 2,//不能对var声明的变量使用delete操作符
"no-div-regex": 1,//不能使用看起来像除法的正则表达式/=foo/
"no-dupe-keys": 2,//在创建对象字面量时不允许键重复 {a:1,a:1}
"no-dupe-args": 2,//函数参数不能重复
"no-duplicate-case": 2,//switch中的case标签不能重复
"no-else-return": 2,//如果if语句里面有return,后面不能跟else语句
"no-empty": 2,//块语句中的内容不能为空
"no-empty-character-class": 2,//正则表达式中的[]内容不能为空
"no-empty-label": 2,//禁止使用空label
"no-eq-null": 2,//禁止对null使用==或!=运算符
"no-eval": 1,//禁止使用eval
"no-ex-assign": 2,//禁止给catch语句中的异常参数赋值
"no-extend-native": 2,//禁止扩展native对象
"no-extra-bind": 2,//禁止不必要的函数绑定
"no-extra-boolean-cast": 2,//禁止不必要的bool转换
"no-extra-parens": 2,//禁止非必要的括号
"no-extra-semi": 2,//禁止多余的冒号
"no-fallthrough": 1,//禁止switch穿透
"no-floating-decimal": 2,//禁止省略浮点数中的0 .5 3.
"no-func-assign": 2,//禁止重复的函数声明
"no-implicit-coercion": 1,//禁止隐式转换
"no-implied-eval": 2,//禁止使用隐式eval
"no-inline-comments": 0,//禁止行内备注
"no-inner-declarations": [2, "functions"],//禁止在块语句中使用声明(变量或函数)
"no-invalid-regexp": 2,//禁止无效的正则表达式
"no-invalid-this": 2,//禁止无效的this,只能用在构造器,类,对象字面量
"no-irregular-whitespace": 2,//不能有不规则的空格
"no-iterator": 2,//禁止使用__iterator__ 属性
"no-label-var": 2,//label名不能与var声明的变量名相同
"no-labels": 2,//禁止标签声明
"no-lone-blocks": 2,//禁止不必要的嵌套块
"no-lonely-if": 2,//禁止else语句内只有if语句
"no-loop-func": 1,//禁止在循环中使用函数(如果没有引用外部变量不形成闭包就可以)
"no-mixed-requires": [0, false],//声明时不能混用声明类型
"no-mixed-spaces-and-tabs": [2, false],//禁止混用tab和空格
"linebreak-style": [0, "windows"],//换行风格
"no-multi-spaces": 1,//不能用多余的空格
"no-multi-str": 2,//字符串不能用\换行
"no-multiple-empty-lines": [1, {"max": 2}],//空行最多不能超过2行
"no-native-reassign": 2,//不能重写native对象
"no-negated-in-lhs": 2,//in 操作符的左边不能有!
"no-nested-ternary": 0,//禁止使用嵌套的三目运算
"no-new": 1,//禁止在使用new构造一个实例后不赋值
"no-new-func": 1,//禁止使用new Function
"no-new-object": 2,//禁止使用new Object()
"no-new-require": 2,//禁止使用new require
"no-new-wrappers": 2,//禁止使用new创建包装实例,new String new Boolean new Number
"no-obj-calls": 2,//不能调用内置的全局对象,比如Math() JSON()
"no-octal": 2,//禁止使用八进制数字
"no-octal-escape": 2,//禁止使用八进制转义序列
"no-param-reassign": 2,//禁止给参数重新赋值
"no-path-concat": 0,//node中不能使用__dirname或__filename做路径拼接
"no-plusplus": 0,//禁止使用++,--
"no-process-env": 0,//禁止使用process.env
"no-process-exit": 0,//禁止使用process.exit()
"no-proto": 2,//禁止使用__proto__属性
"no-redeclare": 2,//禁止重复声明变量
"no-regex-spaces": 2,//禁止在正则表达式字面量中使用多个空格 /foo bar/
"no-restricted-modules": 0,//如果禁用了指定模块,使用就会报错
"no-return-assign": 1,//return 语句中不能有赋值表达式
"no-script-url": 0,//禁止使用javascript:void(0)
"no-self-compare": 2,//不能比较自身
"no-sequences": 0,//禁止使用逗号运算符
"no-shadow": 2,//外部作用域中的变量不能与它所包含的作用域中的变量或参数同名
"no-shadow-restricted-names": 2,//严格模式中规定的限制标识符不能作为声明时的变量名使用
"no-spaced-func": 2,//函数调用时 函数名与()之间不能有空格
"no-sparse-arrays": 2,//禁止稀疏数组, [1,,2]
"no-sync": 0,//nodejs 禁止同步方法
"no-ternary": 0,//禁止使用三目运算符
"no-trailing-spaces": 1,//一行结束后面不要有空格
"no-this-before-super": 0,//在调用super()之前不能使用this或super
"no-throw-literal": 2,//禁止抛出字面量错误 throw "error";
"no-undef": 1,//不能有未定义的变量
"no-undef-init": 2,//变量初始化时不能直接给它赋值为undefined
"no-undefined": 2,//不能使用undefined
"no-unexpected-multiline": 2,//避免多行表达式
"no-underscore-dangle": 1,//标识符不能以_开头或结尾
"no-unneeded-ternary": 2,//禁止不必要的嵌套 var isYes = answer === 1 ? true : false;
"no-unreachable": 2,//不能有无法执行的代码
"no-unused-expressions": 2,//禁止无用的表达式
"no-unused-vars": [2, {"vars": "all", "args": "after-used"}],//不能有声明后未被使用的变量或参数
"no-use-before-define": 2,//未定义前不能使用
"no-useless-call": 2,//禁止不必要的call和apply
"no-void": 2,//禁用void操作符
"no-var": 0,//禁用var,用let和const代替
"no-warning-comments": [1, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],//不能有警告备注
"no-with": 2,//禁用with
"array-bracket-spacing": [2, "never"],//是否允许非空数组里面有多余的空格
"arrow-parens": 0,//箭头函数用小括号括起来
"arrow-spacing": 0,//=>的前/后括号
"accessor-pairs": 0,//在对象中使用getter/setter
"block-scoped-var": 0,//块语句中使用var
"brace-style": [1, "1tbs"],//大括号风格
"callback-return": 1,//避免多次调用回调什么的
"camelcase": 2,//强制驼峰法命名
"comma-dangle": [2, "never"],//对象字面量项尾不能有逗号
"comma-spacing": 0,//逗号前后的空格
"comma-style": [2, "last"],//逗号风格,换行时在行首还是行尾
"complexity": [0, 11],//循环复杂度
"computed-property-spacing": [0, "never"],//是否允许计算后的键名什么的
"consistent-return": 0,//return 后面是否允许省略
"consistent-this": [2, "that"],//this别名
"constructor-super": 0,//非派生类不能调用super,派生类必须调用super
"curly": [2, "all"],//必须使用 if(){} 中的{}
"default-case": 2,//switch语句最后必须有default
"dot-location": 0,//对象访问符的位置,换行的时候在行首还是行尾
"dot-notation": [0, { "allowKeywords": true }],//避免不必要的方括号
"eol-last": 0,//文件以单一的换行符结束
"eqeqeq": 2,//必须使用全等
"func-names": 0,//函数表达式必须有名字
"func-style": [0, "declaration"],//函数风格,规定只能使用函数声明/函数表达式
"generator-star-spacing": 0,//生成器函数*的前后空格
"guard-for-in": 0,//for in循环要用if语句过滤
"handle-callback-err": 0,//nodejs 处理错误
"id-length": 0,//变量名长度
"indent": [2, 4],//缩进风格
"init-declarations": 0,//声明时必须赋初值
"key-spacing": [0, { "beforeColon": false, "afterColon": true }],//对象字面量中冒号的前后空格
"lines-around-comment": 0,//行前/行后备注
"max-depth": [0, 4],//嵌套块深度
"max-len": [0, 80, 4],//字符串最大长度
"max-nested-callbacks": [0, 2],//回调嵌套深度
"max-params": [0, 3],//函数最多只能有3个参数
"max-statements": [0, 10],//函数内最多有几个声明
"new-cap": 2,//函数名首行大写必须使用new方式调用,首行小写必须用不带new方式调用
"new-parens": 2,//new时必须加小括号
"newline-after-var": 2,//变量声明后是否需要空一行
"object-curly-spacing": [0, "never"],//大括号内是否允许不必要的空格
"object-shorthand": 0,//强制对象字面量缩写语法
"one-var": 1,//连续声明
"operator-assignment": [0, "always"],//赋值运算符 += -=什么的
"operator-linebreak": [2, "after"],//换行时运算符在行尾还是行首
"padded-blocks": 0,//块语句内行首行尾是否要空行
"prefer-const": 0,//首选const
"prefer-spread": 0,//首选展开运算
"prefer-reflect": 0,//首选Reflect的方法
"quotes": [1, "single"],//引号类型 `` "" ''
"quote-props":[2, "always"],//对象字面量中的属性名是否强制双引号
"radix": 2,//parseInt必须指定第二个参数
"id-match": 0,//命名检测
"require-yield": 0,//生成器函数必须有yield
"semi": [2, "always"],//语句强制分号结尾
"semi-spacing": [0, {"before": false, "after": true}],//分号前后空格
"sort-vars": 0,//变量声明时排序
"space-after-keywords": [0, "always"],//关键字后面是否要空一格
"space-before-blocks": [0, "always"],//不以新行开始的块{前面要不要有空格
"space-before-function-paren": [0, "always"],//函数定义时括号前面要不要有空格
"space-in-parens": [0, "never"],//小括号里面要不要有空格
"space-infix-ops": 0,//中缀操作符周围要不要有空格
"space-return-throw-case": 2,//return throw case后面要不要加空格
"space-unary-ops": [0, { "words": true, "nonwords": false }],//一元运算符的前/后要不要加空格
"spaced-comment": 0,//注释风格要不要有空格什么的
"strict": 2,//使用严格模式
"use-isnan": 2,//禁止比较时使用NaN,只能用isNaN()
"valid-jsdoc": 0,//jsdoc规则
"valid-typeof": 2,//必须使用合法的typeof的值
"vars-on-top": 2,//var必须放在作用域顶部
"wrap-iife": [2, "inside"],//立即执行函数表达式的小括号风格
"wrap-regex": 0,//正则表达式字面量用小括号包起来
"yoda": [2, "never"]//禁止尤达条件
}
}
让vscode安装eslint规范的方式去保存
1.安装插件ESLint
2.配置settings.json文件
"editor.codeActionsOnSave":{
"source.fixAll.eslint":true
},
"eslint.format.enable":true,
"eslint.validate":[
"javascript",
"vue",
"html"
],
"[vue]":{
"editor.defaultFormatter":"dbaeumer.vscode-eslint"
}
此时,编写 vue 文件时 编写时,就会自动的按照eslint标准语法进行检测,并标注错误 保存代码时,会自动按照 eslint
语法要求对代码进行修复