答:它是一个对象,用来传递异步操作的信息。
Pending:进行中,Resolved:已完成,Reject:已失败
优点:可以通过.then解决回调地狱的问题,通过try-catch解决问题捕获
1.es6的set方法
var arr = [1,1,8,8,12,12,15,15,16,16];
function unique (arr) {
return Array.from(new Set(arr))
}
console.log(unique(arr))
//[1,8,12,15,16]
2.利用for嵌套for,然后splice去重
function unique(arr){
for(var i=0;i
3.新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组。
function unique(arr) {
var newArr = [];
for(var i = 0;i < arr.length;i++){
if(newArr.indedxof(arr[i]) === -1){
newArr.push(arr[i])
}
}
return newArr
}
- 利用indexOf去重
var arr = [1, 1, 8, 8, 12, 12, 15, 15, 16, 16];
function unlink(arr) {
if (!Array.isArray(arr)) {
console.log('错误!')
return
}
var array = [];
for (var i = 0; i < arr.length; i++) { // 首次遍历数组
if (array.indexOf(arr[i]) === -1) { // 判断索引有没有等于
array.push(arr[i])
}
}
return array
}
console.log(unlink(arr));
5.利用filter
var arr = [1, 1, 8, 8, 12, 12, 15, 15, 16, 16];
function unlink(arr) {
return arr.filter(function (item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
console.log(unlink(arr));
ECMAScript5,即ES5,是ECMAScript的第五次修订,于2009年完成标准化
ECMAScript6,即ES6,是ECMAScript的第六次修订,于2015年完成,也称ES2015
ES6是继ES5之后的一次改进,相对于ES5更加简洁,提高了开发效率
ES6新增的一些特性:
1)let声明变量和const声明常量,两个都有块级作用域
ES5中是没有块级作用域的,并且var有变量提升,在let中,使用的变量一定要进行声明
2)箭头函数
ES6中的函数定义不再使用关键字function(),而是利用了()=>来进行定义
3)模板字符串
模板字符串是增强版的字符串,用反引号(`)标识,可以当作普通字符串使用,也可以用来定义多行字符串
4)解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值
5)for of循环
for...of循环可以遍历数组、Set和Map结构、某些类似数组的对象、对象,以及字符串
6)import、export导入导出
ES6标准中,Js原生支持模块(module)。将JS代码分割成不同功能的小块进行模块化,将不同功能的代码分别写在不同文件中,各模块只需导出公共接口部分,然后通过模块的导入的方式可以在其他地方使用
7)set数据结构
Set数据结构,类似数组。所有的数据都是唯一的,没有重复的值。它本身是一个构造函数
8)... 展开运算符
可以将数组或对象里面的值展开;还可以将多个值收集为一个变量
9)修饰器 @
decorator是一个函数,用来修改类甚至于是方法的行为。修饰器本质就是编译时执行的函数
10)class 类的继承
ES6中不再像ES5一样使用原型链实现继承,而是引入Class这个概念
11)async、await
使用 async/await, 搭配promise,可以通过编写形似同步的代码来处理异步流程, 提高代码的简洁性和可读性
async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成
12)promise
Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理、强大
13)Symbol
Symbol是一种基本类型。Symbol 通过调用symbol函数产生,它接收一个可选的名字参数,该函数返回的symbol是唯一的
14)Proxy代理
使用代理(Proxy)监听对象的操作,然后可以做一些相应事情
var声明变量可以重复声明,而let不可以重复声明
var是不受限于块级的,而let是受限于块级
var会与window相映射(会挂一个属性),而let不与window相映射
var可以在声明的上面访问变量,而let有暂存死区,在声明的上面访问变量会报错
const声明之后必须赋值,否则会报错
const定义不可变的量,改变了就会报错
const和let一样不会与window相映射、支持块级作用域、在声明的上面访问变量会报错
(1)用了箭头函数,this就不是指向window,而是父级(指向是可变的)
(2)不能够使用arguments对象
(3)不能用作构造函数,这就是说不能够使用new命令,否则会抛出一个错误
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数
基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定
在ES5时我们通过反斜杠()来做多行字符串或者字符串一行行拼接。ES6反引号(``)就能解决
类模板字符串的功能
复制代码
let name = 'web';
let age = 10;
let str = '你好,${name} 已经 ${age}岁了'
str = str.replace(/\$\{([^}]*)\}/g,function(){
return eval(arguments[1]);
})
console.log(str);//你好,web 已经 10岁了
应用场景Set用于数据重组,Map用于数据储存
Set:
(1)成员不能重复
(2)只有键值没有键名,类似数组
(3)可以遍历,方法有add, delete,has
Map:
(1)本质上是健值对的集合,类似集合
(2)可以遍历,可以跟各种数据格式转换
ES6的class可以看作是一个语法糖,它的绝大部分功能ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法
复制代码
//定义类
class Point {
constructor(x,y) {
//构造方法
this.x = x; //this关键字代表实例对象
this.y = y;
} toString() {
return '(' + this.x + ',' + this.y + ')';
}
}
promise构造函数是同步执行的,then方法是异步执行的
事件循环中分为宏任务队列和微任务队列
其中setTimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行
promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行
async函数表示函数里面可能会有异步方法,await后面跟一个表达式
async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行
三个状态:pending、fulfilled、reject
两个过程:padding -> fulfilled、padding -> rejected
当pending为rejectd时,会进入catch
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
console.log(2);
})
promise.then(() => {
console.log(3);
})
console.log(4);
复制代码
1 2 4 3
Promise 新建后立即执行,所以会先输出 1,2,而 Promise.then() 内部的代码在 当次 事件循环的 结尾 立刻执行 ,所以会继续输出4,最后输出3
let a = 1;let b = 2;
[a,b] = [b,a];
let name = Symbol('name');
let product = {
[name]:"洗衣机",
"price":799
};
Reflect.ownKeys(product);
let s = new Set();
s.add([1]);
s.add([1]);console.log(s.size);
答案:2
两个数组[1]并不是同一个值,它们分别定义的数组,在内存中分别对应着不同的存储地址,因此并不是相同的值
都能存储到Set结构中,所以size为2
reject 是用来抛出异常,catch 是用来处理异常
reject 是 Promise 的方法,而 catch 是 Promise 实例的方法
reject后的东西,一定会进入then中的第二个回调,如果then中没有写第二个回调,则进入catch
网络异常(比如断网),会直接进入catch而不会进入then的第二个回调
//创建一个Promise的类
class Promise{
constructor(executer){//构造函数constructor里面是个执行器
this.status = 'pending';//默认的状态 pending
this.value = undefined//成功的值默认undefined
this.reason = undefined//失败的值默认undefined
//状态只有在pending时候才能改变
let resolveFn = value =>{
//判断只有等待时才能resolve成功
if(this.status == pending){
this.status = 'resolve';
this.value = value;
}
}
//判断只有等待时才能reject失败
let rejectFn = reason =>{
if(this.status == pending){
this.status = 'reject';
this.reason = reason;
}
}
try{
//把resolve和reject两个函数传给执行器executer
executer(resolve,reject);
}catch(e){
reject(e);//失败的话进catch
}
}
then(onFufilled,onReject){
//如果状态成功调用onFufilled
if(this.status = 'resolve'){
onFufilled(this.value);
}
//如果状态失败调用onReject
if(this.status = 'reject'){
onReject(this.reason);
}
}
}
let arr = [12,43,23,43,68,12];
let item = [...new Set(arr)];
console.log(item);//[12, 43, 23, 68]
let arr = [11,22,33,44,55];
let sum = 0;
for(let i=0;i
async await 是用来解决异步的,async函数是Generator函数的语法糖
使用关键字async来表示,在函数内部使用 await 来表示异步
async函数返回一个 Promise 对象,可以使用then方法添加回调函数
当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
async较Generator的优势:
(1)内置执行器。Generator 函数的执行必须依靠执行器,而 Aysnc 函数自带执行器,调用方式跟普通函数的调用一样
(2)更好的语义。async 和 await 相较于 * 和 yield 更加语义化
(3)更广的适用性。yield命令后面只能是 Thunk 函数或 Promise对象,async函数的await后面可以是Promise也可以是原始类型的值
(4)返回值是 Promise。async 函数返回的是 Promise 对象,比Generator函数返回的Iterator对象方便,可以直接使用 then() 方法进行调用
forEach更多的用来遍历数组
for in 一般常用来遍历对象或json
for of数组对象都可以遍历,遍历对象需要通过和Object.keys()
for in循环出的是key,for of循环出的是value
导入通过import关键字
复制代码
// 只导入一个
import {sum} from "./example.js"
// 导入多个
import {sum,multiply,time} from "./exportExample.js"
// 导入一整个模块
import * as example from "./exportExample.js"
复制代码
导出通过export关键字
复制代码
//可以将export放在任何变量,函数或类声明的前面
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
//也可以使用大括号指定所要输出的一组变量
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};
//使用export default时,对应的import语句不需要使用大括号
let bosh = function crs(){}
export default bosh;
import crc from 'crc';
//不使用export default时,对应的import语句需要使用大括号
let bosh = function crs(){}
export bosh;
import {crc} from 'crc';
1.首先共享状态放在state里面存储
2.然后组件可以用state中的数据,拿完数据后组件可能会执行一个异步请求
3.我们把一个异步操作放在一个actions里面,要更改vux的状态,不能直接更改。
4.需要调用mutations,让mutations同步的去更改vuex状态,状态更改视图就渲染了
特点:易用,灵活,高效
vue是一套用于构建用户界面的渐进式框架,核心是一个响应的数据绑定系统
vue是一款MVVM框架,基于双向绑定数据,当数据发生变化时候,vue自身会进行一些运算
特点:简洁轻量、数据驱动、组件化、模块友好
vue.js使用了IE8无法模拟的 ECMAScript 5 特性,没有替代方案
vue在创建vm的时候,会将数据配置到实例中,然后通过Object.defineProperty方法,为数据动态的添加getter与setter方法。
当获取数据的时候,会触发对应的getter方法,当设置数据的时候,触发对应的setter方法。
然后当setter方法触发完成的时候,内部会进一步触发watcher,从而数据改变了,视图则更新操作完毕。
MVVM是Model-View-ViewModel的缩写
Model层代表数据模型
View代表组件视图,负责将数据模型转化成UI展现出来
ViewModel是一个同步 View 和 Model 的对象(双向绑定)
在MVVM中,View和Model之间并没有直接的联系,而是通过ViewModel进行交互,
Model和ViewModel之间的交互是双向的,因此 通过视图操作数据,也能通过数据操作视图
MVC是Model-View- Controller的简写。即模型-视图-控制器,使用MVC的目的是为了将M和V相分离
MVVM与MVC最大的区别就是实现了View和Model的自动同步,也就是当Model的属性改变时
我们不用再手动操作Dom来改变View,而是改变后该属性对应View层会自动改变
数据驱动和组件化思想
vue的双向邦定是基于ES5中getter/setter来实现的,而angular是由自己实现一套模版编译规则,需要进行所谓的“脏值”检查,vue则不需要
vue需要提供一个el对象进行实例化,后续的所有作用范围也是在el对象之下,而angular而是整个html页面
Vue的模式是m-v-vm模式,即(model-view-modelView),通过modelView作为中间层,进行双向数据的绑定与变化
1)通过建立虚拟dom树document.createDocumentFragment(),方法创建虚拟dom树
2)一旦被监测的数据改变,会通过Object.defineProperty定义的数据拦截,截取到数据的变化
3)截取到的数据变化,从而通过订阅——发布者模式,触发Watcher(观察者),从而改变虚拟dom的中的具体数据
4)最后通过更新虚拟dom的元素值,从而改变最后渲染dom树的值,完成双向绑定
单页面应用,用户所有的操作都在一个页面完成
优点:无刷新,用户体验好,共享资源只需要请求一次即可,采用组件化的思想,代码结构更加规范化,便于修改和调整
缺点:对搜索引擎不友好、低版本不支持,第一次加载首页耗时相对较长,不能使用浏览器导航按钮,需要自行实现前进后退
相同点:
都支持服务器端渲染、数据驱动视图,状态管理
都有虚拟DOM、组件化开发、通过props参数进行父子组件数据的传递
不同点:
React严格上只针对MVC的C层,Vue则是MVVM模式
虚拟DOM方面
vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树,而React每当应用的状态被改变时,全部组件都会重新渲染
视图渲染方面
React采用JSX渲染到DOM,vue使用的是template模板
数据绑定方面
vue实现了数据的双向绑定,react数据流动是单向的
state对象方面
react应用中不可变的,需要使用setState方法更新状态
vue中,state对象不是必须的,数据由data属性在vue对象中管理
1)提高开发效率
2)方便重复使用
3)便于协同开发
4)更容易管理维护
创建前/后beforeCreate/created:
在beforeCreated阶段,vue实例的挂载元素el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,el还没有
载入前/后beforeMount/mounted:
在beforeMount阶段,vue实例的el和data都初始化了,但还是挂载之前为虚拟的dom节点,data尚未替换。在mounted阶段,vue实例挂载完成,data成功渲染。
更新前/后beforeUpdate/updated:
当data变化时,会触发beforeUpdate和updated方法。不常用
销毁前/后beforeDestory/destoryed:
beforeDestory是在vue实例销毁前触发,一般在这里要通过removeEventListener解除手动绑定的事件
执行destroy方法后,vue实例已经解除了事件监听以及dom的绑定,但是dom结构依然存在
在被keep-alive包含的组件/路由中,会多出两个生命周期的钩子:
activated 与 deactivated
angular中无法判断数据是否做了更改,所以设置了一些条件,当触发这些条件之后就会执行一个检测来遍历所有的数据,对比刚才更改的地方执行变化
这个检查很不科学而且效率不高,有很多多余的地方
如果想写的css只对当前组件起作用,则在style中写入scoped
v-if和v-show都是用来控制元素的渲染
v-if是根据后面数据的真假,来判断DOM的添加删除等操作
v-show只是在修改元素的css样式(display属性值)
v-if如果初始渲染条件为真,就渲染,反之就不渲染
v-show不管初始条件是否为真,都会被渲染
v-if有更高的切换消耗,不适合做频繁的切换
v-show有更高的初始渲染消耗,适合做频繁的切换
事件修饰符:stop、prevent、self、once
键盘修饰符:enter、space、up、down
表单修饰符修饰符:number、trim、lazy
v-if、v-show、v-for、v-on、v-bind、v-model、v-once
可以
m o u n t 和 e l 两 者 在 使 用 中 没 有 什 么 区 别 , 都 是 将 实 例 化 后 的 v u e 挂 载 到 指 定 的 D O M 元 素 中 如 果 在 实 例 化 v u e 时 指 定 e l , 则 该 v u e 将 会 渲 染 在 e l 对 应 的 D O M 中 没 有 指 定 e l , 则 v u e 实 例 会 处 于 一 种 “ 未 挂 载 ” 的 状 态 , 此 时 通 过 mount和el两者在使用中没有什么区别,都是将实例化后的vue挂载到指定的DOM元素中 如果在实例化vue时指定el,则该vue将会渲染在el对应的DOM中 没有指定el,则vue实例会处于一种“未挂载”的状态,此时通过 mount和el两者在使用中没有什么区别,都是将实例化后的vue挂载到指定的DOM元素中 如果在实例化vue时指定el,则该vue将会渲染在el对应的DOM中 没有指定el,则vue实例会处于一种“未挂载”的状态,此时通过mount来手动执行挂载
...
使用key,vue会基于key的变化重新排列元素顺序,并且移除key不存在的元素。可以做优化处理
is可以扩展原生html元素,也可以绑定动态组件
因为组件是用来复用的,JS 里对象是引用关系,这样作用域没有隔离
而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题
这个概念出现在组件通信。数据从父级流向子级,数据本身还是父级的
如果操作子级要改变父级的数据,只能通过子级告知父级要操作哪个数据
然后让父级去修改自己的数据,修改完毕再传给子级
computed 计算属性,能监听vue中数据的变化,当数据发生变化时候会触发
1、函数形式
computed:{
listenArr(){
//使用data中数据,自动帮你监听数据的变化
//返回的结果就是通过改变数据 做的另一件事情
}
}
2、对象形式
computed:{
listenArr:{
get(){
//获取时候
},
set(newVal){
//修改时候
}
}
}
//当使用get set时候,computed中定义的属性为一个对象
//不使用get set时候,computed中定义的属性可以是一个函数
父级传递子级:
首先在子组件上加一个v-bind:自定义属性,等于父级的数据,子组件通过props方法接收数据
子级传递父级:
在子组件上绑定一个自定义事件,并且传入父级的“事件”处理函数
在子组件内部使用 e m i t 监 听 这 个 自 定 义 事 件 , t h i s . emit监听这个自定义事件,this. emit监听这个自定义事件,this.emit(‘自定义事件名’,参数)
computed能够监听vue数据上的变化,页面上来就执行一次,每改变一次数据就又触发,在操作数据的时候,会派生出另一个事情
watch当指定数据发生变化时候触发。一开始不会触发,只有指定的数据发生变化就又触发一次
watch可以deep深度添加,computed不可以
vue中nextTick可以拿到更新后的DOM元素
如果在mounted下不能准确拿到DOM元素,可以使用nextTick
在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中
全局定义指令:在vue对象的directive方法里面有两个参数,分别为指令名称、函数组件内定义指令:directives
钩子函数: bind(绑定事件出发)、inserted(节点插入时候触发)、update(组件内相关更新)
钩子函数参数: el、binding
1)将公用的JS库通过script标签外部引入,让浏览器并行下载资源文件,提高下载速度
2)在配置 路由时,页面和组件使用懒加载方式引入,在调用组件时再加载对应的js文件
3)加一个首屏 loading 图,提升用户体验
trim:用来过滤前后的空格
number:将用户输入的数据绑定为number类型
lazy:使用了这个修饰符将会从“input事件”变成change事件进行同步
全局导航钩子:to、from、next
组件内的钩子:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
单个路由独享的钩子:beforeEnter
声明式(标签跳转)
编程式( js 跳转) router.push(‘index’)
webpack 中提供了 require.ensure()来实现按需加载。以前引入路由是通过 import 这样的方式引入,改为 const 定义的方式进行引入
不进行页面按需加载引入方式
import home from ‘…/common/home.vue’
进行页面按需加载的引入方式:
const home = r => require.ensure( [], () => r (require(’…/common/home.vue’)))
router是VueRouter的一个对象,通过Vue.use(VueRouter),和VueRouter构造函数得到一个router的实例对象
route是一个跳转的路由对象,每一个路由都会有一个route对象
是一个局部的对象,可以获取对应的name,path,params,query等
vue文件的一个加载器,跟template/js/style转换成js模块
用途:js可以写es6、style样式可以scss或less、template可以加jade等
query和params两者都是在Vue路由中传参
query用path来引入,params只能用name来传递,不能使用path
query更像get请求(地址栏会显示参数),而params更像post(不会在地址栏显示参数)
在router目录下的index.js文件中,对path属性加上/:id
使用router对象的params.id
在 VueRouter 的参数中使用 children 配置,这样就可以很好的实现路由嵌套
children 里面是子路由,当然子路由里面还可以继续嵌套子路由
vue-router模块的router-link组件
$route.name 当前路由的名称
$route.path 当前路由对象的路径
$route.meta 在路由里面埋一个字段,当切换路由时候把信息传过去
$route.query 查询信息包含路由中查询参数的键值对
$route.hash 当前路径的哈希值,带#
$route.params 预设的变量,切换时候通过parmas带过去某个id的值
main.js是入口文件
app.vue是一个主组件
view放视图页面
components放组件
router是定义路由相关的配置
assets文件夹是放静态资源
第一步:在components目录新建你的组件文件
第二步:在需要用的页面(组件)中导入
第三步:注入到vue的子组件的components属性上面
第四步:在template视图view中使用
vue实例从创建到销毁的过程
也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期
作用:生命周期中有多个事件钩子,让我们在控制Vue实例的过程时更容易形成好的逻辑
第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子
beforecreate : 可以在这加个loading事件,在加载实例时触发
created : 初始化完成时的操作,比如结束loading事件,也可以操作异步请求
mounted : 挂载元素,获取到DOM节点,推荐使用nextTick
updated : 如果对数据统一处理,在这里写上相应函数
beforeDestroy : 可以做一个确认停止事件的确认框,关掉定时器
destroyed:当前组件已被删除,清空相关内容
插槽就是vue实现的一套内容分发的API,将插槽元素作为承载分发内容的出口。
在组件模板中默认占一个位置,当使用组件标签时候,组件标签的内容会自动替换掉内容
slot中可以设置一些默认的内容,如果传递了内容则会替换掉,如果没有名字的标签会默认放到default中
computed有get set两个选项
watch有handler deep 是否深度,immeditate 是否立即执行
methods是一个对象,可以在对象中定义一个个方法,能接受参数,而computed不能
computed是可以缓存的,methods不会
computed可以依赖其他computed,甚至是其他组件的data
Vue一般使用template来创建HTML,然后有的时候,我们需要使用javascript来创建html,这时候我们需要使用render函数
render函数return一个createElement组件中的子元素存储在组件实列中
createElement返回的是包含的信息会告诉VUE页面上需要渲染什么样的节点。我们称这样的节点为虚拟DOM
vuex是vue框架中的状态管理器
在main.js引入store,注入,新建了一个目录store,export导出
适用场景:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车
在 created() 钩子函数执行的时候DOM其实并未进行任何渲染,而此时进行 DOM 操作无异于徒劳,
所以在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进Vue.nextTick() 的回调函数中
vuex 就是一个仓库,仓库里放了很多对象。
其中 state 就是数据源存放地,对应于一般 vue 对象里面的 data
state 里面存放的数据是响应式的,vue 组件从 store 读取数据,若是 store 中的数据发生改变,依赖这相数据的组件也会发生改变
它通过 mapState 把全局的 state 和 getters 映射到当前组件的 computed 计算属性
如果请求来的数据不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入 vuex 的 state 里
如果被其他地方复用,请将请求放入 action 里,方便复用,并包装成 promise 返回
Vuex 的思想是 当我们在页面上点击一个按钮,它会触发(dispatch)一个action, action 随后会执行(commit)一个mutation, mutation 立即会改变state,
state 改变以后,我们的页面会state 获取数据,页面发生了变化。 Store 对象,包含了我们谈到的所有内容,action, state, mutation
vue在创建vm的时候,会将数据配置到实例中,然后通过Object.defineProperty方法,为数据动态的添加getter与setter方法。
当获取数据的时候,会触发对应的getter方法,当设置数据的时候,触发对应的setter方法。
然后当setter方法触发完成的时候,内部会进一步触发watcher,从而数据改变了,视图则更新操作完毕。
1、watch的监听只是单个的监听,每次监听只能监听一个变量的修改,不能同时监听多个变量的同时更改。
而计算属性computed可以依赖多个数据的变化(并且只跟它所依赖项进行关联)
2、当需要在数据变化时执行异步或开销较大的操作时,只能选择采用watch去实现
3、与方法的区别就是watch方法里面的数据不会被缓存,并且watch每次渲染页面时都要重新执行该函数。computed的计算属性只有相关依赖发生变化才会进行更新
v-on:click v-on:keyup v-on:mouseover
v-on:click ===> @click
v-if 动态创建/删除
v-show 动态显示/隐藏
**v-cloak指令:这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕**
在Vue里可以使用mixins来做代码的抽离复用,便于维护
一个mixin其实就是一个纯粹的对象,上面挂载着抽离出来的配置,
/在某一个实例中,通过mixins选项导入后,此实例就拥有导入的mixin的配置
并且不会与原配置相互覆盖,而是合并到一起
.stop 阻止事件冒泡
.prevent 取消事件默认行为
.once 事件触发一次
.self 只能在自身上面触发
.capture
.passive
绑定了value属性与监听了input事件,一旦数据改变了,input事件执行了。
v-model.lazy 默认是同步更新,如果加了lazy修饰符,等光标离开之后才会更新视图
v-model.number 这个值无法被parseFloat解析的话,会原样返回,如果可以被解析,则返回解析后的结果
v-model.trim 去掉前后空格
v-if是将元素从dom移出,重新插入。
v-show是简单的进行css样式切换
内部必须是一个函数返回对象的形式,这样的话就能保证每一个组件里面用到的数据都是唯一的
一个组件的data必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝
答:在操作底层只是dom用过,例如输入框获取焦点
自定义指令的常用钩子函数:
bind inserted update componentUpdated unbind
页面加载时:bind inserted
更新组件时:update componentUpdated
卸载组件时:unbind
重新安装组件时:bind inserted
用到一个css3里的属性:transform:scale()
通过检测地址栏的变化来实现组件的安装与卸载
:调用install方法
SPA的优点:只有一个页面,内部是通过组件的卸载和装载实现页面的切换,用户体验好
MPA的优点:有利于搜索引擎的优化,有利于网站排名,爬取关键信息
其实就是调用了window.onhashchange方法 hash值的切换
本质使用H5的histroy.pushState方法来更改url
在某些情况下,当路由跳转前或跳转后、进入、离开某一个路由前、后,需要做某些操作,就可以使用路由钩子来监听路由的变化
会在任意路由跳转前执行,next一定要记着执行,不然路由不能跳转了
this. $router.push()
懒加载也叫延迟加载,即在需要的时候进行加载,随用随载。在单页应用中,如果没有应用懒加载,运用webpack打包后的文件将会异常的大,造成进入首页时,需要加载的内容过多,延时过长,不利于用户体验,而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时。
Vue.extend
Vue.nextTick
Vue.set
Vue.delete
Vue.directive
Vue.filter
Vue.component
Vue.use
Vue.mixin
Vue.compile
Vue.observable
Vue.version
可以用来封装拦截器
1、请求之前的拦截:一般可以在发送请求的时候,携带一些数据给他们
2、响应之后的拦截:可以根据响应后的不同的状态码进行判断分析,给前端返回不同的符合条件的数据
一般在登录的功能的时候,通过vue-router提供的路由守卫拦截,当token失效时,需要用拦截器进行拦截
1null是个对象,undefined就是undefined;2、undefined是定义了没赋值,null是空指针,给数组等引用数据类型赋空值,减少内存开销;后期事件解绑只需要给其赋null
vuex是存在内存中,不是永久存储
一刷新,数据就不存在了
1. 应用层级的状态应该集中到单个 store 对象中。
2. 提交 **mutation** 是更改状态的唯一方法,并且这个过程是同步的。
3. 异步逻辑都应该封装到 **action** 里面。
router-link组件避免了不必要的重渲染,它只更新变化的部分从而减少DOM性能消耗
反观标签,每次跳转都得重渲染一次
是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。并且在切换组件的过程中只会经过created一次
所谓代理服务器就是位于发起请求的客户端与原始服务器端之间的一台跳板服务器,正向代理可以隐藏客户端,反向代理可以隐藏原始服务器。
正向代理:
用户知道目标服务器地址,但由于网络限制等原因,无法直接访问。这时候需要先连接代理服务器,然后再由代理服务器访问目标服务器。
反向代理:
反向代理对用户则是不可知的,比如我们访问百度网站,百度的代理服务器对外的域名为 https://www.baidu.com 。具体内部的服务器节点我们不知道,现实中我们通过访问百度的代理服务器后,代理服务器给我们转发请求到他们N多的服务器节点中的一个给我们进行搜索后将结果返回。
客户端渲染不利于SEO搜索引擎优化
服务端渲染是可以被爬虫抓取到的,客户端异步渲染是很难被爬虫抓取到的
服务端渲染对SEO友好,经过服务端渲染的页面,在网络传输的时候,传输的是一个真实的页面,所以爬虫就会对这个页面中的关键数据进行分析、收录。
服务端渲染缺点就是 对服务器压力比较大
客户端渲染减轻了服务器端的渲染压力,能够实现前后端分离开发
客户端渲染缺点就是 对SEO相当的不友好
有两个优点:
第一点:能更快获取到服务端数据,减少页面 loading 时间;
第二点:放在 created 中有助于一致性,因为ssr 不支持 beforeMount 、mounted 钩子函数。
1、react严格上只能算是MVC的view层,vue则是MVVM模式。2、虚拟DOM不一样,vue会跟踪每一个组件的依赖关系,不需要重新渲 染整个组件树。3、数据绑不同,vue实现了数据的双向绑定,react数据流动是单向的。
通过 vuex-persistedstate这个插件,来实现将数据存储到本地
key的主要作用是为了高效的更新虚拟DOM,原理是vue在patch的过程中可以精准判断两个节点是否一致,从而避免了频繁更新不同元素,减少DOM操作量,提高性能。若不设置key,当一个元素添加的时候,后面的元素便会经历卸载与重新加载的过程。为了避免出错,尽量不要使用index作为key
1、减少HTTP请求达到性能优化
2、使用字体图标
3、适当合并脚本和样式表
4、CSS Sprites技术
5、页面渲染优化
6、手机端优化
1.,标记鲜明,容易维护 组件化后,我们只需要对对应的组件进行维护,不会影响到其它文件。而且文件结构清楚,方便后台人员的使用。
2.,块状化结构,减少css 的书写,并且方便扩展 块状化结构,一个模块一个标识,标识加元素标签定位书写css,减少大量的class
标签必须要闭合
2) 在外层只能有一个根元素
3) class ==> className
4) style ==> { {backgroundColor:‘red’}}
data写成函数返回对象格式
组件是具体的,模块是抽象的
对普通DOM元素进行底层操作,
a、页面加载时:bind inserted
b、更新组件:update componentUpdated
c、卸载组件:unbind
d、重新安装组件:bind inserted
回调函数就是函数以一个指针的形式以提供给其他函数调用,是同步操作,以便在获取某个数据之后,再进行其他操作
哪里用过呢?
计时器,请求数据的axios,ajax,fetch,resource,nextTick
Vue.nextTick() or this.$nextTick()
下次DOM更新循环结束后执行的延迟回调,在修改数据之后,立即使用这个方法,可以获取更新后的DOM
同时可用此方法进行轮播图的实例化
axios.request是集成所有请求的一个集合体
namespaced:true
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
Vue.use===this.$use
vue-cli + vue2.0 + vuex + vue-router + axios + element-ui
拦截器、路由守卫
Vuex存储的数据在页面刷新时,自动销毁。localstorage永久存储数据,可以 通过 vuex-persistedstate这个插件,将vue-x的数据存储在localstorage内
发生请求之前的一个拦截器:可以携带请求头给后端
服务器响应之后的拦截器:后台给前端返回很多的状态码,根据返回的状态码进行业务逻辑实现
1、在vue的组件中,通过dispatch触发actions提交修改数据的操作
2、然后通过actions的commit来触发mutations来修改数据
3、mutation接受commit请求,就会通过Mutate来修改state里面的数据
4、最后由store触发每一个调用它的组件的更新
冒泡:e.stoppropagation||e.cancelBubble=true
阻止默认事件:e.preventDefault||window.event.returnValue=false
视口宽:document.documentElement.clientWidth||document.body.clientWidth
git init 初始化仓库
git add 添加文件到暂存区
git commit 提交暂存区到本地仓库
git push 上传代码
webpack:把项目当做一个整体,通过一个给定的主文件,Webpack将从这个文件开始找到你的项目的所有依赖文件,使用orders处理它们,最后打包为一个浏览器可识别的js文件
gulp:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,工具之后可以自动替你完成这些任务
53、jsonp跨域请求的原理:
动态创建script标签,然后利用script的src不受同源策略约束来跨域请求数据
1、let和const命令
2、变量的结构赋值:var [a,b,c] = [1,2,3]
3、字符串新增方法:trim()取消空格,includs查询是否找到了参数字符串
4、新增数组方法:find(回调函数)查找第一个符合条件的元素,findIndex()查找符合下标的元素,copyWithin(a,b,c)从位置a替换成b-c的元素,include()方法,查找是否包含元素的方法,forEach
5、新型字符串``
6、集合:set,map
1.export
//a.js
export const str = "blablabla~";
export function log(sth) {
return sth;
}
对应的导入方式:
//b.js
import { str, log } from 'a'; //也可以分开写两次,导入的时候带花括号
2.export default
//a.js
const str = "blablabla~";
export default str;
对应的导入方式:
//b.js
import str from 'a'; //导入的时候没有花括号
//a.js
let sex = "boy";
export default sex(sex不能加大括号)
//原本直接export sex外部是无法识别的,加上default就可以了.但是一个文件内最多只能有一个export default。
其实此处相当于为sex变量值"boy"起了一个系统默认的变量名default,自然default只能有一个值,所以一个文件内不能有多个export default。
// b.js
本质上,a.js文件的export default输出一个叫做default的变量,然后系统允许你为它取任意名字。所以可以为import的模块起任何变量名,且不需要用大括号包含
import any from "./a.js"
import any12 from "./a.js"
console.log(any,any12) // boy,boy
//第一种导出方式
// export const a = 10;
// export const b =20;
导入方式
import {a,b} from"./a"
//第二种导出方式
// const a = 10;
// const b = 20
// export{
// a,b
// }
导入方式
import {a,b} from"./a"
或者
import * as A from "./a"
第三种导出方式
export default{
a:1,
b:2
}
导入方式:
import A(写什么都行) from "./a"
或者
import B(写什么都行) from "./a"
这两种方式输出结果一样
//例如:if(xxx){require()}是可行的,但import必须放在开头
什么是闭包?1函数嵌套函数2内部函数可以调用外部函数的形参和变量3 内部函数的变量不会被内存回收机制回收
优点:1避免全局污染2、声明私有成员3希望有一个变量常驻在内存当中
缺点:闭包使得函数中的变量都被保存在内存当中,内存消耗很大,可能会造成内存泄漏,还有定时器未关
域名,端口号,协议。处理方法:
1、代理。2、通过后端CORS解决跨域问题:Access-Control-Allow-Origin。3、jsonP
filter是对原数组的元素进行筛选,将合格元素组成一个新数组进行返回;map是对每一个元素进行修改,返回修改后的数据组成一个新数组。换言之:filter之后的数组元素一定是之前元素中的一部分,map可能是修改后的数据
每次生成唯一的值,也就是每次都不相等,
作用:让代码的可读性更强。
foreach遍历数组;forin遍历数组和对象;forof遍历set,map,String,arguments,Nodelist
闭包,定时器未关
bind不会被立即调用,而是返回一个新的函数,称为绑定函数,其内的this指向就是bind的第一个参数,第二个及以后的参数作为绑定函数的参数来调用
apply和call都是为了改变某个函数运行时的上下文而存在的,如果你要传递的参数不多,则可以使用fn.call(thisObj, arg1, arg2 ...)
如果你要传递的参数很多,则可以用数组将参数整理好调用fn.apply(thisObj, [arg1, arg2 ...])
transform:scale(0.5)
1设置父元素为table-cell,添加virtical-align:center;
2设置子元素为inline-block,给父元素添加line-height=height,text-align:center;
3父元素添加position:relative;子元素添加:position:absolute;top:0;bottom:0;left:0;right:0;margin:auto;
4父元素添加position:relative;子元素添加:left:50%;margin-left:w/2;top:50%,margin-top:h/2;
5给父元素添加flex,添加item-alitn:center
由多级父对象逐级继承,形成的链式结构,通过__ proto __实现子类共用原型链上的属性和方法,向上查找
1、利用set去重
var arr = […new Set(arr)]
2、利用forfor循环,然后splice去重
3、利用indexOf去重
4、利用sort排序,将每相邻两位进行对比,然后生成新数组
在一段连续操作结束后,处理回调,利用clearTimeout和setTimeout实现。只关注一定时间连续触发的事件只在最后一次执行
例如:搜索框搜索输入,手机号、邮箱输入验证,窗口大小resize
//非立即执行
function debounce(func,delay){
let timer;
return function(){
let args = arguments;
timer&&clearTimeout(timer);
timer = setTimeout(()=>{
func.apply(this,args)
},wait)
}
}
//立即执行
function debounce(func,delay){
let timer;
return function(){
let context = this
let args = arguments;
timer&&clearTimeout(timer)
let callNow = !timer;
timer = setTimeout(()=>{
timer = null
},delay)
callNow&&func.apply(context,args)
}
}
在一段连续操作中,每段时间只执行一次,频率较高的事件中使用来提高性能
滚动加载,高频点击
function throttle(func, wait) {
let previous = 0;
return function() {
let now = Date.now();
let context = this;
let args = arguments;
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}
}
通俗的讲就是:当访问一个页面的时候,先把img元素或是其他元素的背景图片路径替换成一张大小为1*1px图片的路径(这样就只需请求一次),只有当图片出现在浏览器的可视区域内时,才设置图片正真的路径,让图片显示出来。这就是图片懒加载。
针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。
一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。
1、for…in循环配合递归封装函数实现深拷贝
function deepClone(target) {
// 定义一个变量
let result;
// 如果当前需要深拷贝的是一个对象的话
if (typeof target === 'object') {
// 如果是一个数组的话
if (Array.isArray(target)) {
result = []; // 将result赋值为一个数组,并且执行遍历
for (let i in target) {
// 递归克隆数组中的每一项
result.push(deepClone(target[i]))
}
// 判断如果当前的值是null的话;直接赋值为null
} else if (target === null) {
result = null;
// 判断如果当前的值是一个RegExp对象的话,直接赋值
} else if (target.constructor === RegExp) {
result = target;
} else {
// 否则是普通对象,直接for in循环,递归赋值对象的所有值
result = {};
for (let i in target) {
result[i] = deepClone(target[i]);
}
}
// 如果不是对象的话,就是基本数据类型,那么直接赋值
} else {
result = target;
}
// 返回最终结果
return result;
}
2、JSON的parse和stringfy方法实现
let newObj = JSON.parse(JSON.stringify(obj))
3、JQ的extend方法实现
$.extend([deep],target,…object)
deep 表示深拷贝,Boolean
target 目标对象
…object 需要进行合并的对象
Object.assign只是浅拷贝,可以用来对象合并
21、一个元素绑定多个事件,通过addEventlistener
window.onloage = function(){
document.getElementById("btn").addEventListener('click',function(){alert(1)},false);
document.getElementById("btn").addEventListener('click',function(){alert(1)},false);
}
第一个参数是事件名称,没有on,第二个参数是回调函数,其实是浏览器调用的,第三个是是否捕获阶段触发
addEventlistener不支持IE8一下,可以用attachEvent方法来替代
function flatten(arr) {
return arr.reduce((result, item)=> {
return result.concat(Array.isArray(item) ? flatten(item) : item);
}, []);
}
柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
函数封装:
function curry(fn){
let len = fn.length;
return function temp(){
let args = [...arguments];
if (args.length>=len){
return fn(...args);
}else{
return function(){
return temp(...args,...arguments)
}
}
}
}
轮播图出现划不动情况,因为在created里面实例化
React.Fiber的方法很简单—分片。将用时很长的任务分成很多小片,每一个小片的用时耗时很短,虽然总任务的时间依然很长,但是在每一个小片执行完成之后,都会给其他小片执行的机会,这样唯一的线程就不会拥堵,其他任务依然有运行的机会
1、编写jsx代码
2、jsx代码内部通过babel提供的核心方法react.createElement()
3、将jsx代码转成虚拟dom
4、ReactDOM.render()将虚拟dom转成真实dom
JSX代码 — > 使用react构造组件,bable进行编译(React.createElement方法)—> JavaScript对象(虚拟dom) — ReactDOM.render()函数进行渲染
—>真实DOM元素 —>插入页面
key很关键!!!
key帮助React识别哪些元素改变了,比如被添加或者删除,因此应当给数组中的每一个元素赋予一个确定的标识
虚拟dom对比的时候,加key可以避免出错。如果不加key,当一个元素添加的时候,后面的元素就会经历卸载与重新加载的过程
为了避免出错,所以我们在开发过程中应该尽量避免用index作为key值,除非我知道index是不变的
在改变数据变化时,React都会重构整个虚拟dom树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行真实DOM的更新。
尽管每一次都需要构造完整的虚拟DOM树,但因为虚拟DOM是存在于内存数据,性能是极高的,而对实际DOM的操作仅仅是Diff部分,因而能够达到提高性能的目的
setState,通过该函数,就可以实现数据改变,视图层也发生变化,但也存在小BUG,同时运行多个setState时,不会逐个运行,就像Object.assign的浅拷贝一样,后面调用的setState会覆盖同一周期的setState
this.setState((prevState)=>{
return{
count:prevState.count+1
}
})
(1)在类组件中,通过this.setState({count:this.state.count+1})
内部经过Object.assign()在一个短时间内进行合并,例如添加商品,看似多次,实际只添加一次
(2)在方法组件中,通过使用useState这个hooks去定义状态
const [count,setCount]=React.useState(1)
setCount(count+1)
相当于类组件中的写法:
constructor{
super()
this.state={
count:1
}
}
setCount=()=>{
this.setState({
count:this.state.count+1
})
}
受控组件:受到数据的控制,使得React成为唯一的数据源
class App extends React.Component{
constructor(){
super()
this.state={
value:'123'
}
}
submit=(e)=>{
e.preventDefault()
console.log(this.state.value)
}
handleChange = (e)=>{
this.setState({
value:e.target.value
})
}
render(){
return (
)
}
}
ReactDOM.render( ,document.getElementById("app"))
非受控组件:只需要在DOM元素上通过ref进行绑定取值即可
class App extends React.Component{
submit = (e)=>{
e.preventDefault()
console.log(this.textInput.value)
}
render(){
return(
)
}
}
ReactDOM.render( ,document.getElementById("app"))
1、 1)标签必须要闭合
2) 在外层只能有一个根元素
3) class ==> className
4) style ==> { {backgroundColor:‘red’}}
5) onClick
onClick={()=>{alert(1)}}
注意: this的指向 建议采用箭头函数(因为箭头函数内部的this与定义这个函数的外部this是同一个)
6)input的value值
{this.setState({value:e.target.value})}} />
如果只是简单的显示值,就用defaultvalue,
7)checkbox checked必须配合onChange事件,写成受控组件的形式,否则就采用defaultchecked
8)label的属性 for==>htmlFor
9)jsx的注释:{/* jsx代码*/}
10)jsx的原理:将编写的jsx代码通过babel的React.CreatElement方法编译成虚拟dom,进而通过ReactDOM.render方法将虚拟dom渲染成真实dom
父组件通过属性的方式将将自己的状态传递给子组件,子组件通过props接受父组件的状态
父组件可以将一个更改自身状态的方法传递给子组件,然后子组件通过props接收后进行调用,相当于父组件的方法被执行了,从而改变自身的状态
这个思想搬到代码里就是 EventHub。其主要的功能是就发布事件(on 监听)和订阅事件(trigger 触发)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qihSCfjO-1602246600722)(C:\Users\liuli\AppData\Roaming\Typora\typora-user-images\image-20200902220155528.png)]
https://gitee.com/rh_hg/json-server?_from=gitee_search
npm i json-server -g
json-server --version
json-server .\data.json --port 4000 -w
如果后端没有帮助我们配置跨域处理,需要我们前端手动的进行代理配置
node_modules>react-scripts>config>devServer.js
proxy:{
"/api":{
target:"http://47.96.0.211:9000",
changeOrigin:true,
pathRewrite:{
"^/api":""
}
}
}
但是有问题? 后续安装新的模块的时候,内部yarn.lock文件实时的检测node_modules下面的文件是否手动的
更改过,如果更改的话,重新变成初始状态。
解决方案一:
可以通过 yarn eject 进行react-scripts相关配置文件的抽离操作。
报错?
.git删掉
git init
git add *
git commit -m "first commit"
git push
再去进行 yarn eject Y
解决方案二:
yarn add http-proxy-middleware
src/setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app){
app.use("/api",
createProxyMiddleware({ target: 'http://47.96.0.211:9000', changeOrigin: true , pathRewrite:{ "^/api":"" } })
)
}
只在初始化的时候执行一次
作用:
**1、可以用来初始化组件状态。2可以给一些事件函数绑定this **
在这个钩子函数里面,钩子函数属于类,前面需要加static修饰,所以不能访问this
这个方法返回会什么,状态里的属性就会变成什么
如果你的组件的某个状态就想由外部传入的属性进行关联控制,希望属性变化了,组件内部的状态也发生变化,那么就把这个状态变成派生状态,使用此钩子函数即可,这样,组件内部不能改变该属性。
render什么时候被执行?
1初始化的时候被执行。2当组件内部调用setState时。3当外部调用props改变时。4forceUpdate(强制更新)时
只在初始化的过程中执行一次
17.x版本中不推荐使用的钩子函数
这个钩子函数受到React16.x Fiber协调算法影响,导致这个钩子函数可能会执行多次,如果异步请求放在这里,执行多次显然是不科学的
1、getDerivedStateFromProps
在这个钩子函数里面,钩子函数属于类,前面需要加static修饰,所以不能访问this
这个方法返回会什么,状态里的属性就会变成什么
如果你的组件的某个状态就想由外部传入的属性进行关联控制,希望属性变化了,组件内部的状态也发生变化,那么就把这个状态变成派生状态,使用此钩子函数即可,这样,组件内部不能改变该属性。
2、shouldComponentUpdate(相当于一个过滤网,只将发生改变的数据进行render更新)
询问是否进行更新操作,false,不进行render重新渲染操作,通过减少render的执行次数来提升react的性能
在类组件中:
Component+shouldComponentUpdate===PureComponent
可以用PureComponeng来实现,不过纯组件用的是浅拷贝,会判断地址是否一样:
基本类型:根据外部传入的数据,新的数据与旧的数据是否一致?如果一致,render就不会执行
引用类型:根据外部传入的数据,新的数据与旧的数据地址是否一致,render也不会执行
再去进行更新操作
在方法组件中
可以用***React.memo(functional Component)***
3、render
4、getSnapshotBeforeUpdate
必须配合componentDidUpdate一起使用,并且可以返回一个更新之前的值;一般用于聊天室的滚动
5、componentDidUpdate
轮播图的实例化在此函数当中,
在该钩子函数当中请求数据后,在进行虚拟DOM的对比,对比成功后才会有真实DOM元素。如果将实例化放在componentDidMount中,name虚拟DOM对比的时候就会进行实例化,这样是不对的
(欢迎补充提供意见)