Vue3快速上手1.Vue3简介2020年9月18日，Vue.js发布3.0版本，代号：One Piece（海贼王）耗时2年多、2600+次提交、30+个RFC、600+次PR、99位贡献者github上的tags地址：https://github.com/vuejs/vue-next/releases/tag/v3.0.02.Vue3带来了什么1.性能的提升打包大小减少41%初次渲染快55%, 更新渲染快133%内存减少54%......2.源码的升级使用Proxy代替defineProperty实现响应式重写虚拟DOM的实现和Tree-Shaking......3.拥抱TypeScriptVue3可以更好的支持TypeScript4.新的特性Composition API（组合API）setup配置ref与reactivewatch与watchEffectprovide与inject......新的内置组件FragmentTeleportSuspense其他改变新的生命周期钩子data 选项应始终被声明为一个函数移除keyCode支持作为 v-on 的修饰符......一、创建Vue3.0工程1.使用 vue-cli 创建官方文档：https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create## 查看@vue/cli版本，确保@vue/cli版本在4.5.0以上 vue --version ## 安装或者升级你的@vue/cli npm install -g @vue/cli ## 创建 vue create vue_test ## 启动 cd vue_test npm run serve2.使用 vite 创建官方文档：https://v3.cn.vuejs.org/guide/installation.html#vitevite官网：https://vitejs.cn什么是vite？—— 新一代前端构建工具。优势如下：开发环境中，无需打包操作，可快速的冷启动。轻量快速的热重载（HMR）。真正的按需编译，不再等待整个应用编译完成。传统构建 与 vite构建对比图## 创建工程 npm init vite-app <project-name> ## 进入工程目录 cd <project-name> ## 安装依赖 npm install ## 运行 npm run dev二、常用 Composition API官方文档: https://v3.cn.vuejs.org/guide/composition-api-introduction.html1.拉开序幕的setup理解：Vue3.0中一个新的配置项，值为一个函数。setup是所有Composition API（组合API）“ 表演的舞台 ”。组件中所用到的：数据、方法等等，均要配置在setup中。setup函数的两种返回值：若返回一个对象，则对象中的属性、方法, 在模板中均可以直接使用。（重点关注！）若返回一个渲染函数：则可以自定义渲染内容。（了解）注意点：尽量不要与Vue2.x配置混用Vue2.x配置（data、methos、computed...）中可以访问到setup中的属性、方法。但在setup中不能访问到Vue2.x配置（data、methos、computed...）。如果有重名, setup优先。setup不能是一个async函数，因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。（后期也可以返回一个Promise实例，但需要Suspense和异步组件的配合）2.ref函数作用: 定义一个响应式的数据语法: const xxx = ref(initValue)创建一个包含响应式数据的引用对象（reference对象，简称ref对象）。JS中操作数据： xxx.value模板中读取数据: 不需要.value，直接： {{xxx}} 备注：接收的数据可以是：基本类型、也可以是对象类型。基本类型的数据：响应式依然是靠Object.defineProperty()的get与set完成的。对象类型的数据：内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive函数。3.reactive函数作用: 定义一个对象类型的响应式数据（基本类型不要用它，要用ref函数）语法：const 代理对象= reactive(源对象)接收一个对象（或数组），返回一个代理对象（Proxy的实例对象，简称proxy对象）reactive定义的响应式数据是“深层次的”。内部基于 ES6 的 Proxy 实现，通过代理对象操作源对象内部数据进行操作。4.Vue3.0中的响应式原理vue2.x的响应式实现原理：对象类型：通过Object.defineProperty()对属性的读取、修改进行拦截（数据劫持）。数组类型：通过重写更新数组的一系列方法来实现拦截。（对数组的变更方法进行了包裹）。Object.defineProperty(data, 'count', { get () {}, set () {} })存在问题：新增属性、删除属性, 界面不会更新。直接通过下标修改数组, 界面不会自动更新。Vue3.0的响应式实现原理:通过Proxy（代理）: 拦截对象中任意属性的变化, 包括：属性值的读写、属性的添加、属性的删除等。通过Reflect（反射）: 对源对象的属性进行操作。MDN文档中描述的Proxy与Reflect：Proxy：https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/ProxyReflect：https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflectnew Proxy(data, { // 拦截读取属性值 get (target, prop) { return Reflect.get(target, prop) }, // 拦截设置属性值或添加新属性 set (target, prop, value) { return Reflect.set(target, prop, value) }, // 拦截删除属性 deleteProperty (target, prop) { return Reflect.deleteProperty(target, prop) } }) proxy.name = 'tom' 5.reactive对比ref从定义数据角度对比：ref用来定义：基本类型数据。reactive用来定义：对象（或数组）类型数据。备注：ref也可以用来定义对象（或数组）类型数据, 它内部会自动通过reactive转为代理对象。从原理角度对比：ref通过Object.defineProperty()的get与set来实现响应式（数据劫持）。reactive通过使用Proxy来实现响应式（数据劫持）, 并通过Reflect操作源对象内部的数据。从使用角度对比：ref定义的数据：操作数据需要.value，读取数据时模板中直接读取不需要.value。reactive定义的数据：操作数据与读取数据：均不需要.value。6.setup的两个注意点setup执行的时机在beforeCreate之前执行一次，this是undefined。setup的参数props：值为对象，包含：组件外部传递过来，且组件内部声明接收了的属性。context：上下文对象attrs: 值为对象，包含：组件外部传递过来，但没有在props配置中声明的属性, 相当于 this.$attrs。slots: 收到的插槽内容, 相当于 this.$slots。emit: 分发自定义事件的函数, 相当于 this.$emit。7.计算属性与监视1.computed函数与Vue2.x中computed配置功能一致写法import {computed} from 'vue' setup(){ ... //计算属性——简写 let fullName = computed(()=>{ return person.firstName + '-' + person.lastName }) //计算属性——完整 let fullName = computed({ get(){ return person.firstName + '-' + person.lastName }, set(value){ const nameArr = value.split('-') person.firstName = nameArr[0] person.lastName = nameArr[1] } }) }2.watch函数与Vue2.x中watch配置功能一致两个小“坑”：监视reactive定义的响应式数据时：oldValue无法正确获取、强制开启了深度监视（deep配置失效）。监视reactive定义的响应式数据中某个属性时：deep配置有效。//情况一：监视ref定义的响应式数据 watch(sum,(newValue,oldValue)=>{ console.log('sum变化了',newValue,oldValue) },{immediate:true}) //情况二：监视多个ref定义的响应式数据 watch([sum,msg],(newValue,oldValue)=>{ console.log('sum或msg变化了',newValue,oldValue) }) /* 情况三：监视reactive定义的响应式数据 若watch监视的是reactive定义的响应式数据，则无法正确获得oldValue！！ 若watch监视的是reactive定义的响应式数据，则强制开启了深度监视 */ watch(person,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) },{immediate:true,deep:false}) //此处的deep配置不再奏效 //情况四：监视reactive定义的响应式数据中的某个属性 watch(()=>person.job,(newValue,oldValue)=>{ console.log('person的job变化了',newValue,oldValue) },{immediate:true,deep:true}) //情况五：监视reactive定义的响应式数据中的某些属性 watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{ console.log('person的job变化了',newValue,oldValue) },{immediate:true,deep:true}) //特殊情况 watch(()=>person.job,(newValue,oldValue)=>{ console.log('person的job变化了',newValue,oldValue) },{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性，所以deep配置有效3.watchEffect函数watch的套路是：既要指明监视的属性，也要指明监视的回调。watchEffect的套路是：不用指明监视哪个属性，监视的回调中用到哪个属性，那就监视哪个属性。watchEffect有点像computed：但computed注重的计算出来的值（回调函数的返回值），所以必须要写返回值。而watchEffect更注重的是过程（回调函数的函数体），所以不用写返回值。//watchEffect所指定的回调中用到的数据只要发生变化，则直接重新执行回调。 watchEffect(()=>{ const x1 = sum.value const x2 = person.age console.log('watchEffect配置的回调执行了') })8.生命周期vue2.x的生命周期vue3.0的生命周期Vue3.0中可以继续使用Vue2.x中的生命周期钩子，但有有两个被更名：beforeDestroy改名为 beforeUnmountdestroyed改名为 unmountedVue3.0也提供了 Composition API 形式的生命周期钩子，与Vue2.x中钩子对应关系如下：beforeCreate===>setup()created=======>setup()beforeMount ===>onBeforeMountmounted=======>onMountedbeforeUpdate===>onBeforeUpdateupdated =======>onUpdatedbeforeUnmount ==>onBeforeUnmountunmounted =====>onUnmounted9.自定义hook函数什么是hook？—— 本质是一个函数，把setup函数中使用的Composition API进行了封装。类似于vue2.x中的mixin。自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。10.toRef作用：创建一个 ref 对象，其value值指向另一个对象中的某个属性。语法：const name = toRef(person,'name')应用: 要将响应式对象中的某个属性单独提供给外部使用时。扩展：toRefs 与toRef功能一致，但可以批量创建多个 ref 对象，语法：toRefs(person)三、其它 Composition API1.shallowReactive 与 shallowRefshallowReactive：只处理对象最外层属性的响应式（浅响应式）。shallowRef：只处理基本数据类型的响应式, 不进行对象的响应式处理。什么时候使用?如果有一个对象数据，结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。如果有一个对象数据，后续功能不会修改该对象中的属性，而是生新的对象来替换 ===> shallowRef。2.readonly 与 shallowReadonlyreadonly: 让一个响应式数据变为只读的（深只读）。shallowReadonly：让一个响应式数据变为只读的（浅只读）。应用场景: 不希望数据被修改时。3.toRaw 与 markRawtoRaw：作用：将一个由reactive生成的响应式对象转为普通对象。使用场景：用于读取响应式对象对应的普通对象，对这个普通对象的所有操作，不会引起页面更新。markRaw：作用：标记一个对象，使其永远不会再成为响应式对象。应用场景:有些值不应被设置为响应式的，例如复杂的第三方类库等。当渲染具有不可变数据源的大列表时，跳过响应式转换可以提高性能。4.customRef作用：创建一个自定义的 ref，并对其依赖项跟踪和更新触发进行显式控制。实现防抖效果： {{keyword}} 5.provide 与 inject作用：实现祖与后代组件间通信套路：父组件有一个 provide 选项来提供数据，后代组件有一个 inject 选项来开始使用这些数据具体写法：祖组件中：setup(){ ...... let car = reactive({name:'奔驰',price:'40万'}) provide('car',car) ...... }后代组件中：setup(props,context){ ...... const car = inject('car') return {car} ...... }6.响应式数据的判断isRef: 检查一个值是否为一个 ref 对象isReactive: 检查一个对象是否是由 reactive 创建的响应式代理isReadonly: 检查一个对象是否是由 readonly 创建的只读代理isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理四、Composition API 的优势1.Options API 存在的问题使用传统OptionsAPI中，新增或者修改一个需求，就需要分别在data，methods，computed里修改 。 2.Composition API 的优势我们可以更加优雅的组织我们的代码，函数。让相关功能的代码更加有序的组织在一起。 五、新的组件1.Fragment在Vue2中: 组件必须有一个根标签在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中好处: 减少标签层级, 减小内存占用2.Teleport什么是Teleport？—— Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。<teleport to="移动位置"> 我是一个弹窗 关闭弹窗 </teleport>3.Suspense等待异步组件时渲染一些额外内容，让应用有更好的用户体验使用步骤：异步引入组件import {defineAsyncComponent} from 'vue' const Child = defineAsyncComponent(()=>import('./components/Child.vue'))使用Suspense包裹组件，并配置好default 与 fallback 我是App组件 <Suspense> <Child/> 加载中..... </Suspense> 六、其他1.全局API的转移Vue 2.x 有许多全局 API 和配置。例如：注册全局组件、注册全局指令等。//注册全局组件 Vue.component('MyButton', { data: () => ({ count: 0 }), template: ' Clicked {{ count }} times. ' }) //注册全局指令 Vue.directive('focus', { inserted: el => el.focus() }Vue3.0中对这些API做出了调整：将全局的API，即：Vue.xxx调整到应用实例（app）上2.x 全局 API（Vue）3.x 实例 API (app)Vue.config.xxxxapp.config.xxxxVue.config.productionTip移除Vue.componentapp.componentVue.directiveapp.directiveVue.mixinapp.mixinVue.useapp.useVue.prototypeapp.config.globalProperties2.其他改变data选项应始终被声明为一个函数。过度类名的更改：Vue2.x写法.v-enter, .v-leave-to { opacity: 0; } .v-leave, .v-enter-to { opacity: 1; }Vue3.x写法.v-enter-from, .v-leave-to { opacity: 0; } .v-leave-from, .v-enter-to { opacity: 1; }移除keyCode作为 v-on 的修饰符，同时也不再支持config.keyCodes移除v-on.native修饰符父组件中绑定事件<my-component v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" />子组件中声明自定义事件 移除过滤器（filter）过滤器虽然这看起来很方便，但它需要一个自定义语法，打破大括号内表达式是 “只是 JavaScript” 的假设，这不仅有学习成本，而且有实现成本！建议用方法调用或计算属性去替换过滤器。......扫描二维码，在手机上阅读！
笔记脚手架文件结构 ├── node_modules ├── public │ ├── favicon.ico: 页签图标 │ └── index.html: 主页面 ├── src │ ├── assets: 存放静态资源 │ │ └── logo.png │ │── component: 存放组件 │ │ └── HelloWorld.vue │ │── App.vue: 汇总所有组件 │ │── main.js: 入口文件 ├── .gitignore: git版本管制忽略的配置 ├── babel.config.js: babel的配置文件 ├── package.json: 应用包配置文件 ├── README.md: 应用描述文件 ├── package-lock.json：包版本控制文件关于不同版本的Vuevue.js与vue.runtime.xxx.js的区别：vue.js是完整版的Vue，包含：核心功能 + 模板解析器。vue.runtime.xxx.js是运行版的Vue，只包含：核心功能；没有模板解析器。因为vue.runtime.xxx.js没有模板解析器，所以不能使用template这个配置项，需要使用render函数接收到的createElement函数去指定具体内容。vue.config.js配置文件使用vue inspect > output.js可以查看到Vue脚手架的默认配置。使用vue.config.js可以对脚手架进行个性化定制，详情见：https://cli.vuejs.org/zhref属性被用来给元素或子组件注册引用信息（id的替代者）应用在html标签上获取的是真实DOM元素，应用在组件标签上是组件实例对象（vc）使用方式：打标识： ..... 或 <School ref="xxx"></School>获取：this.$refs.xxxprops配置项功能：让组件接收外部传过来的数据传递数据：<Demo name="xxx"/>接收数据：第一种方式（只接收）：props:['name'] 第二种方式（限制类型）：props:{name:String}第三种方式（限制类型、限制必要性、指定默认值）：props:{ name:{ type:String, //类型 required:true, //必要性 default:'老王' //默认值 } }备注：props是只读的，Vue底层会监测你对props的修改，如果进行了修改，就会发出警告，若业务需求确实需要修改，那么请复制props的内容到data中一份，然后去修改data中的数据。mixin(混入)功能：可以把多个组件共用的配置提取成一个混入对象使用方式：第一步定义混合：{ data(){....}, methods:{....} .... }第二步使用混入：​ 全局混入：Vue.mixin(xxx) ​ 局部混入：mixins:['xxx'] 插件功能：用于增强Vue本质：包含install方法的一个对象，install的第一个参数是Vue，第二个以后的参数是插件使用者传递的数据。定义插件：对象.install = function (Vue, options) { // 1. 添加全局过滤器 Vue.filter(....) // 2. 添加全局指令 Vue.directive(....) // 3. 配置全局混入(合) Vue.mixin(....) // 4. 添加实例方法 Vue.prototype.$myMethod = function () {...} Vue.prototype.$myProperty = xxxx }使用插件：Vue.use()scoped样式作用：让样式在局部生效，防止冲突。写法： 总结TodoList案例组件化编码流程：​ (1).拆分静态组件：组件要按照功能点拆分，命名不要与html元素冲突。​ (2).实现动态组件：考虑好数据的存放位置，数据是一个组件在用，还是一些组件在用：​ 1).一个组件在用：放在组件自身即可。​ 2). 一些组件在用：放在他们共同的父组件上（状态提升）。​ (3).实现交互：从绑定事件开始。props适用于：​ (1).父组件 ==> 子组件 通信​ (2).子组件 ==> 父组件 通信（要求父先给子一个函数）使用v-model时要切记：v-model绑定的值不能是props传过来的值，因为props是不可以修改的！props传过来的若是对象类型的值，修改对象中的属性时Vue不会报错，但不推荐这样做。webStorage存储内容大小一般支持5MB左右（不同浏览器可能还不一样）浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。相关API：xxxxxStorage.setItem('key', 'value'); 该方法接受一个键和值作为参数，会把键值对添加到存储中，如果键名存在，则更新其对应的值。 xxxxxStorage.getItem('person');​ 该方法接受一个键名作为参数，返回键名对应的值。xxxxxStorage.removeItem('key');​ 该方法接受一个键名作为参数，并把该键名从存储中删除。 xxxxxStorage.clear()​ 该方法会清空存储中的所有数据。备注：SessionStorage存储的内容会随着浏览器窗口关闭而消失。LocalStorage存储的内容，需要手动清除才会消失。xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到，那么getItem的返回值是null。JSON.parse(null)的结果依然是null。组件的自定义事件一种组件间通信的方式，适用于：子组件 ===> 父组件使用场景：A是父组件，B是子组件，B想给A传数据，那么就要在A中给B绑定自定义事件（事件的回调在A中）。绑定自定义事件：第一种方式，在父组件中：<Demo @atguigu="test"/> 或 <Demo v-on:atguigu="test"/>第二种方式，在父组件中：<Demo ref="demo"/> ...... mounted(){ this.$refs.xxx.$on('atguigu',this.test) }若想让自定义事件只能触发一次，可以使用once修饰符，或$once方法。触发自定义事件：this.$emit('atguigu',数据)解绑自定义事件this.$off('atguigu')组件上也可以绑定原生DOM事件，需要使用native修饰符。注意：通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时，回调要么配置在methods中，要么用箭头函数，否则this指向会出问题！全局事件总线（GlobalEventBus）一种组件间通信的方式，适用于任意组件间通信。安装全局事件总线：new Vue({ ...... beforeCreate() { Vue.prototype.$bus = this //安装全局事件总线，$bus就是当前应用的vm }, ...... }) 使用事件总线：接收数据：A组件想接收数据，则在A组件中给$bus绑定自定义事件，事件的回调留在A组件自身。methods(){ demo(data){......} } ...... mounted() { this.$bus.$on('xxxx',this.demo) }提供数据：this.$bus.$emit('xxxx',数据)最好在beforeDestroy钩子中，用$off去解绑当前组件所用到的事件。消息订阅与发布（pubsub）一种组件间通信的方式，适用于任意组件间通信。使用步骤：安装pubsub：npm i pubsub-js引入: import pubsub from 'pubsub-js'接收数据：A组件想接收数据，则在A组件中订阅消息，订阅的回调留在A组件自身。methods(){ demo(data){......} } ...... mounted() { this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息 }提供数据：pubsub.publish('xxx',数据)最好在beforeDestroy钩子中，用PubSub.unsubscribe(pid)去取消订阅。nextTick语法：this.$nextTick(回调函数)作用：在下一次 DOM 更新结束后执行其指定的回调。什么时候用：当改变数据后，要基于更新后的新DOM进行某些操作时，要在nextTick所指定的回调函数中执行。Vue封装的过度与动画作用：在插入、更新或移除 DOM元素时，在合适的时候给元素添加样式类名。图示：写法：准备好样式：元素进入的样式：v-enter：进入的起点v-enter-active：进入过程中v-enter-to：进入的终点元素离开的样式：v-leave：离开的起点v-leave-active：离开过程中v-leave-to：离开的终点使用<transition>包裹要过度的元素，并配置name属性：<transition name="hello"> 你好啊！ </transition>备注：若有多个元素需要过度，则需要使用：<transition-group>，且每个元素都要指定key值。vue脚手架配置代理方法一​ 在vue.config.js中添加如下配置：devServer:{ proxy:"http://localhost:5000" }说明：优点：配置简单，请求资源时直接发给前端（8080）即可。缺点：不能配置多个代理，不能灵活的控制请求是否走代理。工作方式：若按照上述配置代理，当请求了前端不存在的资源时，那么该请求会转发给服务器 （优先匹配前端资源）方法二​ 编写vue.config.js配置具体代理规则：module.exports = { devServer: { proxy: { '/api1': {// 匹配所有以 '/api1'开头的请求路径 target: 'http://localhost:5000',// 代理目标的基础路径 changeOrigin: true, pathRewrite: {'^/api1': ''} }, '/api2': {// 匹配所有以 '/api2'开头的请求路径 target: 'http://localhost:5001',// 代理目标的基础路径 changeOrigin: true, pathRewrite: {'^/api2': ''} } } } } /* changeOrigin设置为true时，服务器收到的请求头中的host为：localhost:5000 changeOrigin设置为false时，服务器收到的请求头中的host为：localhost:8080 changeOrigin默认值为true */说明：优点：可以配置多个代理，且可以灵活的控制请求是否走代理。缺点：配置略微繁琐，请求资源时必须加前缀。插槽作用：让父组件可以向子组件指定位置插入html结构，也是一种组件间通信的方式，适用于 父组件 ===> 子组件 。分类：默认插槽、具名插槽、作用域插槽使用方式：默认插槽：父组件中： <Category> html结构1 </Category> 子组件中： <slot>插槽默认内容...</slot> 具名插槽：父组件中： <Category> html结构1 html结构2 </Category> 子组件中： <slot name="center">插槽默认内容...</slot> <slot name="footer">插槽默认内容...</slot> 作用域插槽：理解：数据在组件的自身，但根据数据生成的结构需要组件的使用者来决定。（games数据在Category组件中，但使用数据所遍历出来的结构由App组件决定）具体编码：父组件中： <Category> {{g}} </Category> <Category> {{g}} </Category> 子组件中： <slot :games="games"></slot> Vuex1.概念​ 在Vue中实现集中式状态（数据）管理的一个Vue插件，对vue应用中多个组件的共享状态进行集中式的管理（读/写），也是一种组件间通信的方式，且适用于任意组件间通信。2.何时使用？​ 多个组件需要共享数据时3.搭建vuex环境创建文件：src/store/index.js//引入Vue核心库 import Vue from 'vue' //引入Vuex import Vuex from 'vuex' //应用Vuex插件 Vue.use(Vuex) //准备actions对象——响应组件中用户的动作 const actions = {} //准备mutations对象——修改state中的数据 const mutations = {} //准备state对象——保存具体的数据 const state = {} //创建并暴露store export default new Vuex.Store({ actions, mutations, state })在main.js中创建vm时传入store配置项...... //引入store import store from './store' ...... //创建vm new Vue({ el:'#app', render: h => h(App), store })4.基本使用初始化数据、配置actions、配置mutations，操作文件store.js//引入Vue核心库 import Vue from 'vue' //引入Vuex import Vuex from 'vuex' //引用Vuex Vue.use(Vuex) const actions = { //响应组件中加的动作 jia(context,value){ // console.log('actions中的jia被调用了',miniStore,value) context.commit('JIA',value) }, } const mutations = { //执行加 JIA(state,value){ // console.log('mutations中的JIA被调用了',state,value) state.sum += value } } //初始化数据 const state = { sum:0 } //创建并暴露store export default new Vuex.Store({ actions, mutations, state, })组件中读取vuex中的数据：$store.state.sum组件中修改vuex中的数据：$store.dispatch('action中的方法名',数据) 或 $store.commit('mutations中的方法名',数据)备注：若没有网络请求或其他业务逻辑，组件中也可以越过actions，即不写dispatch，直接编写commit5.getters的使用概念：当state中的数据需要经过加工后再使用时，可以使用getters加工。在store.js中追加getters配置...... const getters = { bigSum(state){ return state.sum * 10 } } //创建并暴露store export default new Vuex.Store({ ...... getters })组件中读取数据：$store.getters.bigSum6.四个map方法的使用mapState方法：用于帮助我们映射state中的数据为计算属性computed: { //借助mapState生成计算属性：sum、school、subject（对象写法） ...mapState({sum:'sum',school:'school',subject:'subject'}), //借助mapState生成计算属性：sum、school、subject（数组写法） ...mapState(['sum','school','subject']), },mapGetters方法：用于帮助我们映射getters中的数据为计算属性computed: { //借助mapGetters生成计算属性：bigSum（对象写法） ...mapGetters({bigSum:'bigSum'}), //借助mapGetters生成计算属性：bigSum（数组写法） ...mapGetters(['bigSum']) },mapActions方法：用于帮助我们生成与actions对话的方法，即：包含$store.dispatch(xxx)的函数methods:{ //靠mapActions生成：incrementOdd、incrementWait（对象形式） ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'}) //靠mapActions生成：incrementOdd、incrementWait（数组形式） ...mapActions(['jiaOdd','jiaWait']) }mapMutations方法：用于帮助我们生成与mutations对话的方法，即：包含$store.commit(xxx)的函数methods:{ //靠mapActions生成：increment、decrement（对象形式） ...mapMutations({increment:'JIA',decrement:'JIAN'}), //靠mapMutations生成：JIA、JIAN（对象形式） ...mapMutations(['JIA','JIAN']), }备注：mapActions与mapMutations使用时，若需要传递参数需要：在模板中绑定事件时传递好参数，否则参数是事件对象。7.模块化+命名空间目的：让代码更好维护，让多种数据分类更加明确。修改store.jsconst countAbout = { namespaced:true,//开启命名空间 state:{x:1}, mutations: { ... }, actions: { ... }, getters: { bigSum(state){ return state.sum * 10 } } } const personAbout = { namespaced:true,//开启命名空间 state:{ ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { countAbout, personAbout } })开启命名空间后，组件中读取state数据：//方式一：自己直接读取 this.$store.state.personAbout.list //方式二：借助mapState读取： ...mapState('countAbout',['sum','school','subject']),开启命名空间后，组件中读取getters数据：//方式一：自己直接读取 this.$store.getters['personAbout/firstPersonName'] //方式二：借助mapGetters读取： ...mapGetters('countAbout',['bigSum'])开启命名空间后，组件中调用dispatch//方式一：自己直接dispatch this.$store.dispatch('personAbout/addPersonWang',person) //方式二：借助mapActions： ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})开启命名空间后，组件中调用commit//方式一：自己直接commit this.$store.commit('personAbout/ADD_PERSON',person) //方式二：借助mapMutations： ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),## 路由理解： 一个路由（route）就是一组映射关系（key - value），多个路由需要路由器（router）进行管理。前端路由：key是路径，value是组件。1.基本使用安装vue-router，命令：npm i vue-router应用插件：Vue.use(VueRouter)编写router配置项://引入VueRouter import VueRouter from 'vue-router' //引入Luyou 组件 import About from '../components/About' import Home from '../components/Home' //创建router实例对象，去管理一组一组的路由规则 const router = new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home } ] }) //暴露router export default router实现切换（active-class可配置高亮样式）<router-link active-class="active" to="/about">About</router-link>指定展示位置<router-view></router-view>2.几个注意点路由组件通常存放在pages文件夹，一般组件通常存放在components文件夹。通过切换，“隐藏”了的路由组件，默认是被销毁掉的，需要的时候再去挂载。每个组件都有自己的$route属性，里面存储着自己的路由信息。整个应用只有一个router，可以通过组件的$router属性获取到。3.多级路由（多级路由）配置路由规则，使用children配置项：routes:[ { path:'/about', component:About, }, { path:'/home', component:Home, children:[ //通过children配置子级路由 { path:'news', //此处一定不要写：/news component:News }, { path:'message',//此处一定不要写：/message component:Message } ] } ]跳转（要写完整路径）：<router-link to="/home/news">News</router-link>4.路由的query参数传递参数 <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link> <router-link :to="{ path:'/home/message/detail', query:{ id:666, title:'你好' } }" >跳转</router-link>接收参数：$route.query.id $route.query.title5.命名路由作用：可以简化路由的跳转。如何使用给路由命名：{ path:'/demo', component:Demo, children:[ { path:'test', component:Test, children:[ { name:'hello' //给路由命名 path:'welcome', component:Hello, } ] } ] }简化跳转： <router-link to="/demo/test/welcome">跳转</router-link> <router-link :to="{name:'hello'}">跳转</router-link> <router-link :to="{ name:'hello', query:{ id:666, title:'你好' } }" >跳转</router-link>6.路由的params参数配置路由，声明接收params参数{ path:'/home', component:Home, children:[ { path:'news', component:News }, { component:Message, children:[ { name:'xiangqing', path:'detail/:id/:title', //使用占位符声明接收params参数 component:Detail } ] } ] }传递参数 <router-link :to="/home/message/detail/666/你好">跳转</router-link> <router-link :to="{ name:'xiangqing', params:{ id:666, title:'你好' } }" >跳转</router-link>特别注意：路由携带params参数时，若使用to的对象写法，则不能使用path配置项，必须使用name配置！接收参数：$route.params.id $route.params.title7.路由的props配置​ 作用：让路由组件更方便的收到参数{ name:'xiangqing', path:'detail/:id', component:Detail, //第一种写法：props值为对象，该对象中所有的key-value的组合最终都会通过props传给Detail组件 // props:{a:900} //第二种写法：props值为布尔值，布尔值为true，则把路由收到的所有params参数通过props传给Detail组件 // props:true //第三种写法：props值为函数，该函数返回的对象中每一组key-value都会通过props传给Detail组件 props(route){ return { id:route.query.id, title:route.query.title } } }8.<router-link>的replace属性作用：控制路由跳转时操作浏览器历史记录的模式浏览器的历史记录有两种写入方式：分别为push和replace，push是追加历史记录，replace是替换当前记录。路由跳转时候默认为push如何开启replace模式：<router-link replace .......>News</router-link>9.编程式路由导航作用：不借助<router-link> 实现路由跳转，让路由跳转更加灵活具体编码：//$router的两个API this.$router.push({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.replace({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.forward() //前进 this.$router.back() //后退 this.$router.go() //可前进也可后退10.缓存路由组件作用：让不展示的路由组件保持挂载，不被销毁。具体编码：<keep-alive include="News"> <router-view></router-view> </keep-alive>11.两个新的生命周期钩子作用：路由组件所独有的两个钩子，用于捕获路由组件的激活状态。具体名字：activated路由组件被激活时触发。deactivated路由组件失活时触发。12.路由守卫作用：对路由进行权限控制分类：全局守卫、独享守卫、组件内守卫全局守卫://全局前置守卫：初始化时执行、每次路由切换前执行 router.beforeEach((to,from,next)=>{ console.log('beforeEach',to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则 next() //放行 }else{ alert('暂无权限查看') // next({name:'guanyu'}) } }else{ next() //放行 } }) //全局后置守卫：初始化时执行、每次路由切换后执行 router.afterEach((to,from)=>{ console.log('afterEach',to,from) if(to.meta.title){ document.title = to.meta.title //修改网页的title }else{ document.title = 'vue_test' } })独享守卫:beforeEnter(to,from,next){ console.log('beforeEnter',to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem('school') === 'atguigu'){ next() }else{ alert('暂无权限查看') // next({name:'guanyu'}) } }else{ next() } }组件内守卫：//进入守卫：通过路由规则，进入该组件时被调用 beforeRouteEnter (to, from, next) { }, //离开守卫：通过路由规则，离开该组件时被调用 beforeRouteLeave (to, from, next) { }13.路由器的两种工作模式对于一个url来说，什么是hash值？—— #及其后面的内容就是hash值。hash值不会包含在 HTTP 请求中，即：hash值不会带给服务器。hash模式：地址中永远带着#号，不美观 。若以后将地址通过第三方手机app分享，若app校验严格，则地址会被标记为不合法。兼容性较好。history模式：地址干净，美观 。兼容性和hash模式相比略差。应用部署上线时需要后端人员支持，解决刷新页面服务端404的问题。扫描二维码，在手机上阅读！
初识Vue： Hello，{{name.toUpperCase()}}，{{address}} 1.想让Vue工作，就必须创建一个Vue实例，且要传入一个配置对象；2.root容器里的代码依然符合html规范，只不过混入了一些特殊的Vue语法；3.root容器里的代码被称为【Vue模板】；4.Vue实例和容器是一一对应的；5.真实开发中只有一个Vue实例，并且会配合着组件一起使用；6.{{xxx}}中的xxx要写js表达式，且xxx可以自动读取到data中的所有属性；7.一旦data中的数据发生改变，那么页面中用到该数据的地方也会自动更新；注意区分：js表达式 和 js代码(语句)1.表达式：一个表达式会产生一个值，可以放在任何一个需要值的地方： (1). a (2). a+b (3). demo(1) (4). x === y ? 'a' : 'b'2.js代码(语句) (1). if(){} (2). for(){}Vue模板语法有2大类：1.插值语法： 功能：用于解析标签体内容。 写法：{{xxx}}，xxx是js表达式，且可以直接读取到data中的所有属性。2.指令语法： 功能：用于解析标签（包括：标签属性、标签体内容、绑定事件.....）。 举例：v-bind:href="xxx" 或 简写为 :href="xxx"，xxx同样要写js表达式，且可以直接读取到data中的所有属性。 备注：Vue中有很多的指令，且形式都是：v-????，此处我们只是拿v-bind举个例子。Vue中有2种数据绑定的方式：1.单向绑定(v-bind)：数据只能从data流向页面。 2.双向绑定(v-model)：数据不仅能从data流向页面，还可以从页面流向data。备注： 1.双向绑定一般都应用在表单类元素上（如：input、select等） 2.v-model:value 可以简写为 v-model，因为v-model默认收集的就是value值。data与el的2种写法1.el有2种写法 (1).new Vue时候配置el属性。 (2).先创建Vue实例，随后再通过vm.$mount('#root')指定el的值。2.data有2种写法 (1).对象式 (2).函数式如何选择：目前哪种写法都可以，以后学习到组件时，data必须使用函数式，否则会报错。3.一个重要的原则：由Vue管理的函数，一定不要写箭头函数，一旦写了箭头函数，this就不再是Vue实例了。MVVM模型M：模型(Model) ：data中的数据V：视图(View) ：模板代码VM：视图模型(ViewModel)：Vue实例观察发现： 1.data中所有的属性，最后都出现在了vm身上。 2.vm身上所有的属性 及 Vue原型上所有属性，在Vue模板中都可以直接使用。数据代理：通过一个对象代理对另一个对象中属性的操作（读/写）1.Vue中的数据代理： 通过vm对象来代理data对象中属性的操作（读/写）2.Vue中数据代理的好处： 更加方便的操作data中的数据3.基本原理： 通过Object.defineProperty()把data对象中所有属性添加到vm上。 为每一个添加到vm上的属性，都指定一个getter/setter。 在getter/setter内部去操作（读/写）data中对应的属性。事件的基本使用：1.使用v-on:xxx 或 @xxx 绑定事件，其中xxx是事件名； 2.事件的回调需要配置在methods对象中，最终会在vm上； 3.methods中配置的函数，不要用箭头函数！否则this就不是vm了； 4.methods中配置的函数，都是被Vue所管理的函数，this的指向是vm 或 组件实例对象； 5.@click="demo" 和 @click="demo($event)" 效果一致，但后者可以传参；Vue中的事件修饰符：1.prevent：阻止默认事件（常用）； 2.stop：阻止事件冒泡（常用）； 3.once：事件只触发一次（常用）； 4.capture：使用事件的捕获模式； 5.self：只有event.target是当前操作的元素时才触发事件； 6.passive：事件的默认行为立即执行，无需等待事件回调执行完毕；键盘事件1.Vue中常用的按键别名： 回车 => enter 删除 => delete (捕获“删除”和“退格”键) 退出 => esc 空格 => space 换行 => tab (特殊，必须配合keydown去使用) 上 => up 下 => down 左 => left 右 => right 2.Vue未提供别名的按键，可以使用按键原始的key值去绑定，但注意要转为kebab-case（短横线命名） 3.系统修饰键（用法特殊）：ctrl、alt、shift、meta (1).配合keyup使用：按下修饰键的同时，再按下其他键，随后释放其他键，事件才被触发。 (2).配合keydown使用：正常触发事件。 4.也可以使用keyCode去指定具体的按键（不推荐） 5.Vue.config.keyCodes.自定义键名 = 键码，可以去定制按键别名计算属性：1.定义：要用的属性不存在，要通过已有属性计算得来。 2.原理：底层借助了Objcet.defineproperty方法提供的getter和setter。 3.get函数什么时候执行？ (1).初次读取时会执行一次。 (2).当依赖的数据发生改变时会被再次调用。 4.优势：与methods实现相比，内部有缓存机制（复用），效率更高，调试方便。 5.备注： 1.计算属性最终会出现在vm上，直接读取使用即可。 2.如果计算属性要被修改，那必须写set函数去响应修改，且set中要引起计算时依赖的数据发生改变。监视(侦听)属性watch：1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作 2.监视的属性必须存在，才能进行监视！！ 3.监视的两种写法： (1).new Vue时传入watch配置 (2).通过vm.$watch监视深度监视：(1).Vue中的watch默认不监测对象内部值的改变（一层）。 (2).配置deep:true可以监测对象内部值改变（多层）。 备注： (1).Vue自身可以监测对象内部值的改变，但Vue提供的watch默认不可以！ (2).使用watch时根据数据的具体结构，决定是否采用深度监视。computed和watch之间的区别： 1.computed能完成的功能，watch都可以完成。 2.watch能完成的功能，computed不一定能完成，例如：watch可以进行异步操作。 两个重要的小原则： 1.所被Vue管理的函数，最好写成普通函数，这样this的指向才是vm 或 组件实例对象。 2.所有不被Vue所管理的函数（定时器的回调函数、ajax的回调函数等、Promise的回调函数），最好写成箭头函数，这样this的指向才是vm 或 组件实例对象。 绑定样式：class样式 写法:class="xxx" xxx可以是字符串、对象、数组。 字符串写法适用于：类名不确定，要动态获取。 对象写法适用于：要绑定多个样式，个数不确定，名字也不确定。 数组写法适用于：要绑定多个样式，个数确定，名字也确定，但不确定用不用。style样式 :style="{fontSize: xxx}"其中xxx是动态值。 :style="[a,b]"其中a、b是样式对象。条件渲染：1.v-if 写法： (1).v-if="表达式" (2).v-else-if="表达式" (3).v-else="表达式" 适用于：切换频率较低的场景。 特点：不展示的DOM元素直接被移除。 注意：v-if可以和:v-else-if、v-else一起使用，但要求结构不能被“打断”。2.v-show 写法：v-show="表达式" 适用于：切换频率较高的场景。 特点：不展示的DOM元素未被移除，仅仅是使用样式隐藏掉3.备注：使用v-if的时，元素可能无法获取到，而使用v-show一定可以获取到。v-for指令: 1.用于展示列表数据 2.语法：v-for="(item, index) in xxx" :key="yyy" 3.可遍历：数组、对象、字符串（用的很少）、指定次数（用的很少）*面试题：react、vue中的key有什么作用？（key的内部原理）虚拟DOM中key的作用： key是虚拟DOM对象的标识，当数据发生变化时，Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较，比较规则如下： 2.对比规则： (1).旧虚拟DOM中找到了与新虚拟DOM相同的key： ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM！ ②.若虚拟DOM中内容变了, 则生成新的真实DOM，随后替换掉页面中之前的真实DOM。 (2).旧虚拟DOM中未找到与新虚拟DOM相同的key 创建新的真实DOM，随后渲染到到页面。用index作为key可能会引发的问题： 1. 若对数据进行：逆序添加、逆序删除等破坏顺序操作: 会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。 2. 如果结构中还包含输入类的DOM： 产生错误DOM更新 ==> 界面有问题。开发中如何选择key?: 1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。 2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作，仅用于渲染列表用于展示，用index作为key是没有问题的。Vue监视数据的原理：vue会监视data中所有层次的数据。如何监测对象中的数据？ 通过setter实现监视，且要在new Vue时就传入要监测的数据。 (1).对象中后追加的属性，Vue默认不做响应式处理 (2).如需给后添加的属性做响应式，请使用如下API： Vue.set(target，propertyName/index，value) 或 vm.$set(target，propertyName/index，value)如何监测数组中的数据？ 通过包裹数组更新元素的方法实现，本质就是做了两件事： (1).调用原生对应的方法对数组进行更新。 (2).重新解析模板，进而更新页面。4.在Vue修改数组中的某个元素一定要用如下方法： 1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse() 2.Vue.set() 或 vm.$set()特别注意：Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性！！！收集表单数据：若： ，则v-model收集的是value值，用户输入的就是value值。 若： ，则v-model收集的是value值，且要给标签配置value值。 若： 1.没有配置input的value属性，那么收集的就是checked（勾选 or 未勾选，是布尔值） 2.配置input的value属性: (1)v-model的初始值是非数组，那么收集的就是checked（勾选 or 未勾选，是布尔值） (2)v-model的初始值是数组，那么收集的的就是value组成的数组备注：v-model的三个修饰符： lazy：失去焦点再收集数据 number：输入字符串转为有效的数字 trim：输入首尾空格过滤过滤器：定义：对要显示的数据进行特定格式化后再显示（适用于一些简单逻辑的处理）。 语法： 1.注册过滤器：Vue.filter(name,callback) 或 new Vue{filters:{}} 2.使用过滤器： 或 v-bind:属性 = "xxx | 过滤器名"备注： 1.过滤器也可以接收额外参数、多个过滤器也可以串联 2.并没有改变原本的数据, 是产生新的对应的数据我们学过的指令： v-bind : 单向绑定解析表达式, 可简写为 :xxx v-model : 双向数据绑定 v-for : 遍历数组/对象/字符串 v-on : 绑定事件监听, 可简写为@ v-if : 条件渲染（动态控制节点是否存存在） v-else : 条件渲染（动态控制节点是否存存在） v-show : 条件渲染 (动态控制节点是否展示)v-text指令： 1.作用：向其所在的节点中渲染文本内容。 2.与插值语法的区别：v-text会替换掉节点中的内容，{{xx}}则不会。v-html指令： 1.作用：向指定节点中渲染包含html结构的内容。 2.与插值语法的区别： (1).v-html会替换掉节点中所有的内容，{{xx}}则不会。 (2).v-html可以识别html结构。 3.严重注意：v-html有安全性问题！！！！ (1).在网站上动态渲染任意HTML是非常危险的，容易导致XSS攻击。 (2).一定要在可信的内容上使用v-html，永不要用在用户提交的内容上！v-cloak指令（没有值）： 1.本质是一个特殊属性，Vue实例创建完毕并接管容器后，会删掉v-cloak属性。 2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。v-once指令： 1.v-once所在节点在初次动态渲染后，就视为静态内容了。 2.以后数据的改变不会引起v-once所在结构的更新，可以用于优化性能。v-pre指令： 1.跳过其所在节点的编译过程。 2.可利用它跳过：没有使用指令语法、没有使用插值语法的节点，会加快编译。定义全局指令Vue.directive('fbind',{ //指令与元素成功绑定时（一上来） bind(element,binding){ element.value = binding.value }, //指令所在元素被插入页面时 inserted(element,binding){ element.focus() }, //指令所在的模板被重新解析时 update(element,binding){ element.value = binding.value } }) 生命周期： 1.又名：生命周期回调函数、生命周期函数、生命周期钩子。 2.是什么：Vue在关键时刻帮我们调用的一些特殊名称的函数。 3.生命周期函数的名字不可更改，但函数的具体内容是程序员根据需求编写的。 4.生命周期函数中的this指向是vm 或 组件实例对象。常用的生命周期钩子：1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。 关于销毁Vue实例 1.销毁后借助Vue开发者工具看不到任何信息。 2.销毁后自定义事件会失效，但原生DOM事件依然有效。 3.一般不会在beforeDestroy操作数据，因为即便操作数据，也不会再触发更新流程了。Vue中使用组件的三大步骤： 一、定义组件(创建组件) 二、注册组件 三、使用组件(写组件标签)一、如何定义一个组件？ 使用Vue.extend(options)创建，其中options和new Vue(options)时传入的那个options几乎一样，但也有点区别；区别如下： 1.el不要写，为什么？ ——— 最终所有的组件都要经过一个vm的管理，由vm中的el决定服务哪个容器。 2.data必须写成函数，为什么？ ———— 避免组件被复用时，数据存在引用关系。备注：使用template可以配置组件结构。二、如何注册组件？ 1.局部注册：靠new Vue的时候传入components选项 2.全局注册：靠Vue.component('组件名',组件)三、编写组件标签： <school></school>几个注意点：1.关于组件名:一个单词组成： 第一种写法(首字母小写)：school 第二种写法(首字母大写)：School多个单词组成： 第一种写法(kebab-case命名)：my-school 第二种写法(CamelCase命名)：MySchool (需要Vue脚手架支持)备注： (1).组件名尽可能回避HTML中已有的元素名称，例如：h2、H2都不行。 (2).可以使用name配置项指定组件在开发者工具中呈现的名字。2.关于组件标签: 第一种写法：<school></school> 第二种写法：<school/>备注：不用使用脚手架时，<school/>会导致后续组件不能渲染。3.一个简写方式： const school = Vue.extend(options) 可简写为：const school = options关于VueComponent： 1.school组件本质是一个名为VueComponent的构造函数，且不是程序员定义的，是Vue.extend生成的。 2.我们只需要写<school/>或<school></school>，Vue解析时会帮我们创建school组件的实例对象，即Vue帮我们执行的：new VueComponent(options)。 3.特别注意：每次调用Vue.extend，返回的都是一个全新的VueComponent！！！！ 4.关于this指向： (1).组件配置中： data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。 (2).new Vue(options)配置中： data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。 5.VueComponent的实例对象，以后简称vc（也可称之为：组件实例对象）。 Vue的实例对象，以后简称vm。一个重要的内置关系1.一个重要的内置关系：VueComponent.prototype.__proto__ === Vue.prototype 2.为什么要有这个关系：让组件实例对象（vc）可以访问到 Vue原型上的属性、方法。扫描二维码，在手机上阅读！
Vue 是什么？Vue (读音 /vjuː/，类似于 view) 是一套用于构建用户界面的渐进式框架vue 的核心库只关注视图层，不仅易于上手，还便于与第三方库或既有项目整合使用Vue将helloworld 渲染到页面上指令本质就是自定义属性Vue中指定都是以 v- 开头v-cloak防止页面加载时出现闪烁问题 {{msg}} v-textv-text指令用于将数据填充到标签中，作用于插值表达式类似，但是没有闪动问题如果数据中有HTML标签会将html标签一并输出注意：此处为单向绑定，数据对象上的值改变，插值会发生变化；但是当插值发生变化并不会影响数据对象的值 {{msg}} v-html用法和v-text 相似 但是他可以将HTML片段填充到标签中可能有安全问题, 一般只在可信任内容上使用 v-html，永不用在用户提交的内容上它与v-text区别在于v-text输出的是纯文本，浏览器不会对其再进行html解析，但v-html会将其当html标签解析后输出。 {{message}} v-pre显示原始信息跳过编译过程跳过这个元素和它的子元素的编译过程。一些静态的内容不需要编译加这个指令可以加快渲染 {{ this will not be compiled }} {{msg}} v-once执行一次性的插值【当数据改变时，插值处的内容不会继续更新】 {{ msg}} 双向数据绑定当数据发生变化的时候，视图也就发生变化当视图发生变化的时候，数据也会跟着同步变化v-modelv-model是一个指令，限制在 、 、 、components中使用 {{msg}} 当输入框中内容改变的时候， 页面上的msg 会自动更新 mvvmMVC 是后端的分层开发概念； MVVM是前端视图层的概念，主要关注于 视图层分离，也就是说：MVVM把前端的视图层，分为了 三部分 Model, View , VM ViewModelm model数据层 Vue 中 数据层 都放在 data 里面v view 视图Vue 中 view 即 我们的HTML页面vm （view-model） 控制器 将数据和视图层建立联系vm 即 Vue 的实例 就是 vmv-on用来绑定事件的形式如：v-on:click 缩写为 @click;v-on事件函数中传入参数 {{num}} 点击1 点击2 事件修饰符在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。Vue 不推荐我们操作DOM 为了解决这个问题，Vue.js 为 v-on 提供了事件修饰符修饰符是由点开头的指令后缀来表示的 ... 使用修饰符时，顺序很重要；相应的代码会以同样的顺序产生。因此，用 v-on:click.prevent.self 会阻止所有的点击，而 v-on:click.self.prevent 只会阻止对元素自身的点击。按键修饰符在做项目中有时会用到键盘事件，在监听键盘事件时，我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符 常用的按键修饰符 .enter => enter键 .tab => tab键 .delete (捕获“删除”和“退格”按键) => 删除键 .esc => 取消键 .space => 空格键 .up => 上 .down => 下 .left => 左 .right => 右 自定义按键修饰符别名在Vue中可以通过config.keyCodes自定义按键修饰符别名 预先定义了keycode 116（即F5）的别名为f5，因此在文字输入框中按下F5，会触发prompt方法 v-bindv-bind 指令被用来响应地更新 HTML 属性v-bind:href 可以缩写为 :href; 绑定对象我们可以给v-bind:class 一个对象，以动态地切换class。注意：v-bind:class指令可以与普通的class特性共存1、 v-bind 中支持绑定一个对象 如果绑定的是一个对象 则 键为 对应的类名 值 为对应data中的数据 学习Vue 学习Node 学习React 对象语法 <sript> var vm= new Vue({ el:'.box', data:{ isColor:true, isSize:true， activeColor:"red", activeSize:"25px", } }) </sript> 绑定class2、 v-bind 中支持绑定一个数组 数组中classA和 classB 对应为data中的数据 这里的classA 对用data 中的 classA 这里的classB 对用data 中的 classB 学习Vue 学习Node 学习React 绑定对象和绑定数组 的区别绑定对象的时候 对象的属性 即要渲染的类名 对象的属性值对应的是 data 中的数据绑定数组的时候数组里面存的是data 中的数据绑定style 绑定样式对象 ' 内联样式 分支结构v-if 使用场景1- 多个元素 通过条件判断展示或者隐藏某个元素。或者多个元素2- 进行两个视图之间的切换 如果flag为true则显示,false不显示! ---------------------------------------------------------- A B C Not A/B/C v-show 和 v-if的区别v-show本质就是标签display设置为none，控制隐藏v-show只编译一次，后面其实就是控制css，而v-if不停的销毁和创建，故v-show性能更好一点。v-if是动态的向DOM树内添加或者删除DOM元素v-if切换有一个局部编译/卸载的过程，切换过程中合适地销毁和重建内部的事件监听和子组件循环结构v-for用于循环的数组里面的值可以是对象，也可以是普通元素 {{ item.message }} 不推荐同时使用 v-if 和 v-for当 v-if 与 v-for 一起使用时，v-for 具有比 v-if 更高的优先级。 {{v + '---' + k + '---' + i}} key 的作用key来给每个节点做一个唯一标识key的作用主要是为了高效的更新虚拟DOM ... 案例选项卡1、 HTML 结构` apple orange lemon `2、 提供的数据 list: [{ id: 1, title: 'apple', path: 'img/apple.png' }, { id: 2, title: 'orange', path: 'img/orange.png' }, { id: 3, title: 'lemon', path: 'img/lemon.png' }]3、 把数据渲染到页面把tab栏 中的数替换到页面上把 data 中 title 利用 v-for 循环渲染到页面上把 data 中 path利用 v-for 循环渲染到页面上 {{item.title}} 4、 给每一个tab栏添加事件,并让选中的高亮4.1 、让默认的第一项tab栏高亮tab栏高亮 通过添加类名active 来实现 （CSS active 的样式已经提前写好）在data 中定义一个 默认的 索引 currentIndex 为 0给第一个li 添加 active 的类名通过动态绑定class 来实现 第一个li 的索引为 0 和 currentIndex 的值刚好相等currentIndex === index 如果相等 则添加类名 active 否则 添加 空类名4.2 、让默认的第一项tab栏对应的div 显示实现思路 和 第一个 tab 实现思路一样 只不过 这里控制第一个div 显示的类名是 current {{item.title}} 4.3 、点击每一个tab栏 当前的高亮 其他的取消高亮给每一个li添加点击事件让当前的索引 index 和 当前 currentIndex 的 值 进项比较如果相等 则当前li 添加active 类名 当前的 li 高亮 当前对应索引的 div 添加 current 当前div 显示 其他隐藏 {{item.title}} 扫描二维码，在手机上阅读！