赞
踩
vue
基本知识点,如下所示:v-html
会有 XSS
风险,会覆盖子组件,代码如下所示:<template> <div> <p>文本插值 {{message}}</p> <p>JS 表达式 {{ flag ? 'yes' : 'no' }} (只能是表达式,不能是 js 语句)</p> <p :id="dynamicId">动态属性 id</p> <hr/> <p v-html="rawHtml"> <span>有 xss 风险</span> <span>【注意】使用 v-html 之后,将会覆盖子元素</span> </p> <!-- 其他常用指令后面讲 --> </div> </template> <script> export default { data() { return { message: 'hello vue', flag: true, rawHtml: '指令 - 原始 html <b>加粗</b> <i>斜体</i>', dynamicId: `id-${Date.now()}` } } } </script>
computed
和 watch
,computed
有缓存,data
不变则不会重新计算。watch
可以进行深度监听,watch
监听引用类型,拿不到 oldVal
,代码如下所示:
<template> <div> <p>num {{num}}</p> <p>double1 {{double1}}</p> <input v-model="double2"/> </div> </template> <script> export default { data() { return { num: 20 } }, computed: { double1() { return this.num * 2 }, double2: { get() { return this.num * 2 }, set(val) { this.num = val/2 } } } } </script>
<template> <div> <input v-model="name"/> <input v-model="info.city"/> </div> </template> <script> export default { data() { return { name: '张三', info: { city: '上海' } } }, watch: { name(oldVal, val) { // eslint-disable-next-line console.log('watch name', oldVal, val) // 值类型,可正常拿到 oldVal 和 val }, info: { handler(oldVal, val) { // eslint-disable-next-line console.log('watch info', oldVal, val) // 引用类型,拿不到 oldVal 。因为指针相同,此时已经指向了新的 val }, deep: true // 深度监听 } } } </script>
class
与 style
,使用动态属性,使用驼峰式写法,代码如下所示:<template> <div> <p :class="{ black: isBlack, yellow: isYellow }">使用 class</p> <p :class="[black, yellow]">使用 class (数组)</p> <p :style="styleData">使用 style</p> </div> </template> <script> export default { data() { return { isBlack: true, isYellow: true, black: 'black', yellow: 'yellow', styleData: { fontSize: '40px', // 转换为驼峰式 color: 'red', backgroundColor: '#ccc' // 转换为驼峰式 } } } } </script> <style scoped> .black { background-color: #999; } .yellow { color: yellow; } </style>
v-if
与 v-else
的用法,可使用变量,也可以使用 ===
表达式,需要区分 v-if
与 v-show
的区别和使用场景,代码如下所示:<template> <div> <p v-if="type === 'a'">A</p> <p v-else-if="type === 'b'">B</p> <p v-else>other</p> <p v-show="type === 'a'">A by v-show</p> <p v-show="type === 'b'">B by v-show</p> </div> </template> <script> export default { data() { return { type: 'a' } } } </script>
v-for
,key
也很重要,不能够乱写。注意的是,v-for
和 v-if
是不能一起使用的,代码如下所示:<template> <div> <p>遍历数组</p> <ul> <li v-for="(item, index) in listArr" :key="item.id"> {{index}} - {{item.id}} - {{item.title}} </li> </ul> <p>遍历对象</p> <ul > <li v-for="(val, key, index) in listObj" :key="key"> {{index}} - {{key}} - {{val.title}} </li> </ul> </div> </template> <script> export default { data() { return { flag: false, listArr: [ { id: 'a', title: '标题1' }, // 数据结构中,最好有 id ,方便使用 key { id: 'b', title: '标题2' }, { id: 'c', title: '标题3' } ], listObj: { a: { title: '标题1' }, b: { title: '标题2' }, c: { title: '标题3' }, } } } } </script>
event
参数,自定义参数。事件修饰符,按键修饰符,观察事件被绑定到哪里,代码如下所示:<template> <div> <p>{{num}}</p> <button @click="increment1">+1</button> <button @click="increment2(2, $event)">+2</button> </div> </template> <script> export default { data() { return { num: 0 } }, methods: { increment1(event) { // eslint-disable-next-line console.log('event', event, event.__proto__.constructor) // 是原生的 event 对象 // eslint-disable-next-line console.log(event.target) // eslint-disable-next-line console.log(event.currentTarget) // 注意,事件是被注册到当前元素的,和 React 不一样 this.num++ // 1. event 是原生的 // 2. 事件被挂载到当前元素 // 和 DOM 事件一样 }, increment2(val, event) { // eslint-disable-next-line console.log(event.target) this.num = this.num + val }, loadHandler() { // do some thing } }, mounted() { window.addEventListener('load', this.loadHandler) }, beforeDestroy() { //【注意】用 vue 绑定的事件,组建销毁时会自动被解绑 // 自己绑定的事件,需要自己销毁!!! window.removeEventListener('load', this.loadHandler) } } </script>
v-model
。常见的表单项 textarea、checkbox、radio、select
,修饰符 lazy、number、trim
,代码如下所示:<template> <div> <p>输入框: {{name}}</p> <input type="text" v-model.trim="name"/> <input type="text" v-model.lazy="name"/> <input type="text" v-model.number="age"/> <p>多行文本: {{desc}}</p> <textarea v-model="desc"></textarea> <!-- 注意,<textarea>{{desc}}</textarea> 是不允许的!!! --> <p>复选框 {{checked}}</p> <input type="checkbox" v-model="checked"/> <p>多个复选框 {{checkedNames}}</p> <input type="checkbox" id="jack" value="Jack" v-model="checkedNames"> <label for="jack">Jack</label> <input type="checkbox" id="john" value="John" v-model="checkedNames"> <label for="john">John</label> <input type="checkbox" id="mike" value="Mike" v-model="checkedNames"> <label for="mike">Mike</label> <p>单选 {{gender}}</p> <input type="radio" id="male" value="male" v-model="gender"/> <label for="male">男</label> <input type="radio" id="female" value="female" v-model="gender"/> <label for="female">女</label> <p>下拉列表选择 {{selected}}</p> <select v-model="selected"> <option disabled value="">请选择</option> <option>A</option> <option>B</option> <option>C</option> </select> <p>下拉列表选择(多选) {{selectedList}}</p> <select v-model="selectedList" multiple> <option disabled value="">请选择</option> <option>A</option> <option>B</option> <option>C</option> </select> </div> </template> <script> export default { data() { return { name: '张三', age: 18, desc: '自我介绍', checked: true, checkedNames: [], gender: 'male', selected: '', selectedList: [] } } } </script>
vue
组件的使用,props
和 $emit
,组件间通讯,自定义事件,组件生命周期,代码如下所示:<template> <div> <Input @add="addHandler"/> <List :list="list" @delete="deleteHandler"/> </div> </template> <script> import Input from './Input' import List from './List' export default { components: { Input, List }, data() { return { list: [ { id: 'id-1', title: '标题1' }, { id: 'id-2', title: '标题2' } ] } }, methods: { addHandler(title) { this.list.push({ id: `id-${Date.now()}`, title }) }, deleteHandler(id) { this.list = this.list.filter(item => item.id !== id) } }, created() { // eslint-disable-next-line console.log('index created') }, mounted() { // eslint-disable-next-line console.log('index mounted') }, beforeUpdate() { // eslint-disable-next-line console.log('index before update') }, updated() { // eslint-disable-next-line console.log('index updated') }, } </script>
<template> <div> <input type="text" v-model="title"/> <button @click="addTitle">add</button> </div> </template> <script> import event from './event' export default { data() { return { title: '' } }, methods: { addTitle() { // 调用父组件的事件 this.$emit('add', this.title) // 调用自定义事件 event.$emit('onAddTitle', this.title) this.title = '' } } } </script>
<template> <div> <ul> <li v-for="item in list" :key="item.id"> {{item.title}} <button @click="deleteItem(item.id)">删除</button> </li> </ul> </div> </template> <script> import event from './event' export default { // props: ['list'] props: { // prop 类型和默认值 list: { type: Array, default() { return [] } } }, data() { return { } }, methods: { deleteItem(id) { this.$emit('delete', id) }, addTitleHandler(title) { // eslint-disable-next-line console.log('on add title', title) } }, created() { // eslint-disable-next-line console.log('list created') }, mounted() { // eslint-disable-next-line console.log('list mounted') // 绑定自定义事件 event.$on('onAddTitle', this.addTitleHandler) }, beforeUpdate() { // eslint-disable-next-line console.log('list before update') }, updated() { // eslint-disable-next-line console.log('list updated') }, beforeDestroy() { // 及时销毁,否则可能造成内存泄露 event.$off('onAddTitle', this.addTitleHandler) } } </script>
import Vue from 'vue'
export default new Vue()
Vue
高级特性,自定义 v-model、$nextTick、slot
、动态和异步组件、keep-alive、mixin
,如下所示:v-model
,代码如下所示:<template> <!-- 例如:vue 颜色选择 --> <input type="text" :value="text1" @input="$emit('change1', $event.target.value)" > <!-- 1. 上面的 input 使用了 :value 而不是 v-model 2. 上面的 change1 和 model.event1 要对应起来 3. text1 属性对应起来 --> </template> <script> export default { model: { prop: 'text1', // 对应 props text1 event: 'change1' }, props: { text1: String, default() { return '' } } } </script>
$nextTick
,Vue
是异步渲染,data
改变之后,DOM
不会立刻渲染,$nextTick
会在 DOM
渲染之后被触发,以获取最新的 DOM
节点,代码如下所示:<template> <div id="app"> <ul ref="ul1"> <li v-for="(item, index) in list" :key="index"> {{item}} </li> </ul> <button @click="addItem">添加一项</button> </div> </template> <script> export default { name: 'app', data() { return { list: ['a', 'b', 'c'] } }, methods: { addItem() { this.list.push(`${Date.now()}`) this.list.push(`${Date.now()}`) this.list.push(`${Date.now()}`) // 1. 异步渲染,$nextTick 待 DOM 渲染完再回调 // 2. 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次 this.$nextTick(() => { // 获取 DOM 元素 const ulElem = this.$refs.ul1 // eslint-disable-next-line console.log( ulElem.childNodes.length ) }) } } } </script>
slot
,插槽的基本使用,作用域插槽,具名插槽,代码如下所示:
<template> <a :href="url"> <slot> 默认内容,即父组件没设置内容时,这里显示 </slot> </a> </template> <script> export default { props: ['url'], data() { return {} } } </script>
<template> <a :href="url"> <slot :slotData="website"> {{website.subTitle}} <!-- 默认值显示 subTitle ,即父组件不传内容时 --> </slot> </a> </template> <script> export default { props: ['url'], data() { return { website: { url: 'http://wangEditor.com/', title: 'wangEditor', subTitle: '轻量级富文本编辑器' } } } } </script>
动态组件,:is = "component-name"
用法,需要根据数据,动态渲染的常见,即组件类型不确定。异步组件,import()
函数,按需加载,异步加载大组件,代码如下所示:
<template> <div> <p>vue 高级特性</p> <hr> <!-- 自定义 v-model --> <!-- <p>{{name}}</p> <CustomVModel v-model="name"/> --> <!-- nextTick --> <!-- <NextTick/> --> <!-- slot --> <!-- <SlotDemo :url="website.url"> {{website.title}} </SlotDemo> --> <!-- <ScopedSlotDemo :url="website.url"> <template v-slot="slotProps"> {{slotProps.slotData.title}} </template> </ScopedSlotDemo> --> <!-- 动态组件 --> <!-- <component :is="NextTickName"/> --> <!-- 异步组件 --> <!-- <FormDemo v-if="showFormDemo"/> <button @click="showFormDemo = true">show form demo</button> --> <!-- keep-alive --> <!-- <KeepAlive/> --> <!-- mixin --> <MixinDemo/> </div> </template> <script> // import CustomVModel from './CustomVModel' // import NextTick from './NextTick' // import SlotDemo from './SlotDemo' // import ScopedSlotDemo from './ScopedSlotDemo' // import KeepAlive from './KeepAlive' import MixinDemo from './MixinDemo' export default { components: { // CustomVModel // NextTick // SlotDemo, // ScopedSlotDemo, // FormDemo: () => import('../BaseUse/FormDemo'), // KeepAlive MixinDemo }, data() { return { name: '张三', website: { url: 'http://baidu.com/', title: '百度', subTitle: '百度前端' }, // NextTickName: "NextTick", showFormDemo: false } } } </script>
keep-alive
,缓存组件,频繁切换,不需要重复渲染,Vue
的常见性能优化,代码如下所示:
<template> <div> <button @click="changeState('A')">A</button> <button @click="changeState('B')">B</button> <button @click="changeState('C')">C</button> <keep-alive> <!-- tab 切换 --> <KeepAliveStageA v-if="state === 'A'"/> <!-- v-show --> <KeepAliveStageB v-if="state === 'B'"/> <KeepAliveStageC v-if="state === 'C'"/> </keep-alive> </div> </template> <script> import KeepAliveStageA from './KeepAliveStateA' import KeepAliveStageB from './KeepAliveStateB' import KeepAliveStageC from './KeepAliveStateC' export default { components: { KeepAliveStageA, KeepAliveStageB, KeepAliveStageC }, data() { return { state: 'A' } }, methods: { changeState(state) { this.state = state } } } </script>
<template> <p>state A</p> </template> <script> export default { mounted() { // eslint-disable-next-line console.log('A mounted') }, destroyed() { // eslint-disable-next-line console.log('A destroyed') } } </script>
<template> <p>state B</p> </template> <script> export default { mounted() { // eslint-disable-next-line console.log('B mounted') }, destroyed() { // eslint-disable-next-line console.log('B destroyed') } } </script>
<template> <p>state C</p> </template> <script> export default { mounted() { // eslint-disable-next-line console.log('C mounted') }, destroyed() { // eslint-disable-next-line console.log('C destroyed') } } </script>
mixin
,多个组件有相同的逻辑,抽离出来。mixin
并不是完美的解决方案,会有一些问题,Vue3
提出的 Composition API
旨在解决这些问题。同样的,mixin
也会存在一些问题,变量来源不明确,不利于阅读,多 mixin
可能会造成命名冲突,mixin
和组件可能出现多对多的关系,复杂度较高,代码如下所示:
<template> <div> <p>{{name}} {{major}} {{city}}</p> <button @click="showName">显示姓名</button> </div> </template> <script> import myMixin from './mixin' export default { mixins: [myMixin], // 可以添加多个,会自动合并起来 data() { return { name: '张三', major: 'web 前端' } }, methods: { }, mounted() { // eslint-disable-next-line console.log('component mounted', this.name) } } </script>
export default { data() { return { city: '上海' } }, methods: { showName() { // eslint-disable-next-line console.log(this.name) } }, mounted() { // eslint-disable-next-line console.log('mixin mounted', this.name) } }
Vue
组件,dispatch、commit、mapState、mapGetters、mapActions 和 mapMutations
也需要熟系。vue-router
,需要熟系路由模式,hash
和 H5 history
,路由配置,动态路由和懒加载。对于 vue-router
路由模式,hash
模式是默认的,如 http://abc.com/#/user/10。H5 history
模式,如 http://abc.com/user/20
,后者需要 server
端支持,因此无特殊需求可选择前者。vue
的原理,从组件化、响应式、vdom
和 diff
、模版编译、渲染过程、前端路由几个方面。asp、jsp、php、nodejs
都有类似的组件化。数据驱动视图,MVVM、setState
。传统组件,只是静态渲染,更新依赖于操作 DOM
,数据驱动视图,vue MVVM
,数据驱动视图,react setState
。vue
的响应式,组件 data
的数据一旦变化,立即触发视图的更新,实现数据驱动视图的第一步。vue
的响应式,核心 API
是 Object.defineProperty
。对于 Object.defineProperty
也存在一些缺点,Vue3.0
启用 Proxy
。Proxy
也存在兼容性的问题,兼容性不好,无法 polyfill
。Object.defineProperty
实现响应式,监听对象,监听数组,复杂对象,深度监听。Object.defineProperty
的缺点,深度监听,需要递归到底,一次性计算量大,无法监听新增属性和删除属性,Vue.set
和 Vue.delete
,无法原生监听数组,需要特殊处理,代码如下所示:// 触发更新视图 function updateView() { console.log('视图更新') } // 重新定义数组原型 const oldArrayProperty = Array.prototype // 创建新对象,原型指向 oldArrayProperty ,再扩展新的方法不会影响原型 const arrProto = Object.create(oldArrayProperty); ['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => { arrProto[methodName] = function () { updateView() // 触发视图更新 oldArrayProperty[methodName].call(this, ...arguments) // Array.prototype.push.call(this, ...arguments) } }) // 重新定义属性,监听起来 function defineReactive(target, key, value) { // 深度监听 observer(value) // 核心 API Object.defineProperty(target, key, { get() { return value }, set(newValue) { if (newValue !== value) { // 深度监听 observer(newValue) // 设置新值 // 注意,value 一直在闭包中,此处设置完之后,再 get 时也是会获取最新的值 value = newValue // 触发更新视图 updateView() } } }) } // 监听对象属性 function observer(target) { if (typeof target !== 'object' || target === null) { // 不是对象或数组 return target } // 污染全局的 Array 原型 // Array.prototype.push = function () { // updateView() // ... // } if (Array.isArray(target)) { target.__proto__ = arrProto } // 重新定义各个属性(for in 也可以遍历数组) for (let key in target) { defineReactive(target, key, target[key]) } } // 准备数据 const data = { name: 'zhangsan', age: 20, info: { address: '北京' // 需要深度监听 }, nums: [10, 20, 30] } // 监听数据 observer(data) // 测试 // data.name = 'lisi' // data.age = 21 // // console.log('age', data.age) // data.x = '100' // 新增属性,监听不到 —— 所以有 Vue.set // delete data.name // 删除属性,监听不到 —— 所有已 Vue.delete // data.info.address = '上海' // 深度监听 data.nums.push(4) // 监听数组
DOM( Virtual DOM)
和 diff
,vdom
是实现 vue
和 react
的重要基石,diff
算法是 vdom
中最核心、最关键的部分,如下所示:DOM
操作非常耗费性能,以前用 jQuery
,可以自行控制 DOM
操作的时机,手动调整。Vue
和 React
是数据驱动视图,如何有效的控制 DOM
操作。vdom
,有了一定复杂度,想减少计算次数比较难。将计算转移为 JS
计算,JS
执行速度很快。vdom
用 JS
模拟 DOM
结构,计算出最小的变更,操作 DOM
snabbdom
学习 vdom
,简洁强大的 vdom
库,易学易用,Vue
参考它实现的 vdom
和 diff
,地址为 https://github.com/snabbdom/snabbdom
。在 Vue3.0
重写了 vdom
的代码,优化了性能diff
算法是 vdom
中最核心、最关键的部分,在日常使用 vue
、react
中可以体现出来,如 key
。diff
即对比,是一个广泛的概念,如 linux diff
命令、git diff
等。两个 JS
对象也可以做 diff
,两棵树做 diff
,如 vdom diff
等等。diff
的时间复杂度 O(n^3)
,第一,遍历 tree 1
;第二,遍历 tree2
;第三,排序。1000
个节点,要计算 1
亿次,算法不可用O(n)
,只比较同一层级,不跨级比较。tag
不相同,则直接删掉重建,不再深度比较。tag
和 key
,两者都相同,则认为是相同节点,不再深度比较。diff
算法的总结,patchVnode、addVnodes removeVnodes、updateChildren
,key
的重要性。vdom
核心概念很重要,h、vnode、patch、diff、key
等等。vdom
存在的价值更加重要,数据驱动视图,控制 DOM
操作。snabbdom
的简单实现,代码如下所示:const snabbdom = window.snabbdom // 定义 patch const patch = snabbdom.init([ snabbdom_class, snabbdom_props, snabbdom_style, snabbdom_eventlisteners ]) // 定义 h const h = snabbdom.h const container = document.getElementById('container') // 生成 vnode const vnode = h('ul#list', {}, [ h('li.item', {}, 'Item 1'), h('li.item', {}, 'Item 2') ]) patch(container, vnode) document.getElementById('btn-change').addEventListener('click', () => { // 生成 newVnode const newVnode = h('ul#list', {}, [ h('li.item', {}, 'Item 1'), h('li.item', {}, 'Item B'), h('li.item', {}, 'Item 3') ]) patch(vnode, newVnode) })
vue
开发中最常用的部分,即与使用相关联的原理。它不是 html
,有指令、插值、JS
表达式,会通过组件渲染和更新过程所去体现出来,如下所示:JS
的 with
语法,能改变 {}
内自由变量的查找方式,vue template complier
将模版编译为 render
函数,执行 render
函数生成 vnode
with
语法,改变 {}
内自由变量的查找规则,当做 obj
属性来查找。如果找不到匹配的 obj
属性,就会报错。with
要慎用,它打破了作用域规则,易读性变差html
,有指令、插值、JS
表达式,能实现判断、循环。html
是标签语言,只有 JS
才能实现判断、循环,图灵完备的。因此,模版一定是转换为某种 JS
代码,即编译模版vue
组件中使用 render
代替 template
。在有些复杂情况中,不能用 template
,可以考虑用 render
。React
一直都用 render
,没有模版,和这里一样。所以,vue
组件中使用 render
代替 template
,代码如下所示:const compiler = require('vue-template-compiler') // 插值 // // const template = `<p>{{message}}</p>` // // with(this){return createElement('p',[createTextVNode(toString(message))])} // // h -> vnode // // createElement -> vnode // // // 表达式 // // const template = `<p>{{flag ? message : 'no message found'}}</p>` // // // with(this){return _c('p',[_v(_s(flag ? message : 'no message found'))])} // // // 属性和动态属性 // // const template = ` // // <div id="div1" class="container"> // // <img :src="imgUrl"/> // // </div> // // ` // // with(this){return _c('div', // // {staticClass:"container",attrs:{"id":"div1"}}, // // [ // // _c('img',{attrs:{"src":imgUrl}})])} // // // 条件 // // const template = ` // // <div> // // <p v-if="flag === 'a'">A</p> // // <p v-else>B</p> // // </div> // // ` // // with(this){return _c('div',[(flag === 'a')?_c('p',[_v("A")]):_c('p',[_v("B")])])} // // 循环 // // const template = ` // // <ul> // // <li v-for="item in list" :key="item.id">{{item.title}}</li> // // </ul> // // ` // // with(this){return _c('ul',_l((list),function(item){return _c('li',{key:item.id},[_v(_s(item.title))])}),0)} // // 事件 // // const template = ` // // <button @click="clickHandler">submit</button> // // ` // // with(this){return _c('button',{on:{"click":clickHandler}},[_v("submit")])} // // v-model // const template = `<input type="text" v-model="name">` // // 主要看 input 事件 // // with(this){return _c('input',{directives:[{name:"model",rawName:"v-model",value:(name),expression:"name"}],attrs:{"type":"text"},domProps:{"value":(name)},on:{"input":function($event){if($event.target.composing)return;name=$event.target.value}}})} // // render 函数 // // 返回 vnode // // patch // // 编译 // const res = compiler.compile(template) // console.log(res.render) // // ---------------分割线-------------- // // // 从 vue 源码中找到缩写函数的含义 // // function installRenderHelpers (target) { // // target._o = markOnce; // // target._n = toNumber; // // target._s = toString; // // target._l = renderList; // // target._t = renderSlot; // // target._q = looseEqual; // // target._i = looseIndexOf; // // target._m = renderStatic; // // target._f = resolveFilter; // // target._k = checkKeyCodes; // // target._b = bindObjectProps; // // target._v = createTextVNode; // // target._e = createEmptyVNode; // // target._u = resolveScopedSlots; // // target._g = bindObjectListeners; // // target._d = bindDynamicKeys; // // target._p = prependModifier; // // }
对于组件的渲染和更新过程,一个组件渲染到页面,修改 data
触发更新,数据驱动视图,对背后原理和流程需要很熟悉。响应式,监听 data
属性 getter
和 setter
,包括数组。模版编译,模版到 render
函数,再到 vnode
。vdom,patch(elem, vnode)
和 patch(vnode, newVnode)
。
对于初次渲染过程,更新过程和异步渲染,也需要很熟悉,如下所示:
render
函数或者是在开发环境已完成,vue-loader
。触发响应式,监听 data
属性 getter
和 setter
。执行 render
函数,生成 vnode
,patch( elem, vnode)
data
,触发 setter
,此前 setter
中已被监听。重新执行 render
函数,生成 newVnode,patch(vnode, newVnode)
$nextTick
,汇总 data
的修改,一次性更新视图。减少 DOM
操作次数,提高性能。异步渲染,$nextTick
待 DOM
渲染完再回调。页面渲染时会将 data
的修改做整合,多次 data
修改只会渲染一次。对于模版的渲染与隔离过程,需要理清渲染和响应式的关系、渲染和模版编译的关系、渲染和 vdom
关系。初次渲染过程,更新过程和异步渲染,同样需要理清。
前端路由原理,对于 SPA
,都需要路由,vue-router
也是 vue
全家桶的标配之一。vue-router
的路由模式、hash
和 H5 history
,如下所示:
hash
的特点,如下所示:
hash
变化会触发网页跳转,即浏览器的前进和后退hash
变化不会刷新页面,SPA
必须的特点hash
永远不会提交到 server
端H5 history
,用 url
规范的路由,但跳转时不刷新页面,history.pushState
和 window.onpopstate
to B
的系统推荐使用 hash
,简单易用,对 url
规范不敏感to C
的系统,可以考虑选择 H5 history
,但需要服务端支持Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。