一些简单的 Vue.js 笔记
2022-05-07 tech front-end vue 27 mins 23 图 9745 字
前端不是我的主业,不过太久不用,捡起来每次都觉得头疼,还是稍微做点笔记吧。
首先是官方文档:https://cn.vuejs.org/v2/guide/,虽然目前到3版本了,还是先了解下v2版本的知识,并不复杂。
一、基本方法
基本就是官网内容。
-
格式
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script> <div id="app"> </div> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } })
-
数据绑定、css绑定、事件绑定和语法糖
v-bind:value=<expression>
单项绑定,语法糖:value=<expression>
v-model:value=<expression>
双向绑定(只用表单),语法糖 v-model=` v-bind:class=xxx
,语法糖:class=<str>
v-on:click.stop=<expression>
事件绑定,语法糖@click.stop=<expression>
.stop
.prevent
-
计算属性
func 里this指针指向的是vm
computed不吃性能,尽量用它
var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { // 计算属性的 getter reversedMessage: function () { // `this` 指向 vm 实例 return this.message.split('').reverse().join('') } }, watch: { // 如果 `question` 发生改变,这个函数就会运行 question: function (newQuestion, oldQuestion) { this.answer = 'Waiting for you to stop typing...' this.debouncedGetAnswer() } }, created: function () { this.debouncedGetAnswer = _.debounce(this.getAnswer, 500) }, methods: { getAnswer: function () { } }, mounted(){ } })
由vue管理的method,要以普通方式定义。
否则应该用箭头函数,例如:
getAnswer: function (()=>{})
此时它的this为window。
另外,指令directive相关的 this 都是 window
-
循环语句和条件语句
循环:
<ul id="example-1"> <li v-for="item in items" :key="item.message"> </li> </ul> <ul id="example-2"> <li v-for="(item, index) in items"> - - </li> </ul> <div v-for="(value, name, index) in object"> . : </div>
条件:
<div v-if="type === 'A'"></div> <div v-else-if="type === 'B'"></div> <div v-else-if="type === 'C'"></div> <div v-else></div>
-
Vue 无法监控数组内容的改变
https://cn.vuejs.org/v2/guide/list.html#数组更新检测
对象因为有setter和getter,修改model可以监控到,view也会变动。
但直接操作数组不奏效,因为数组没有这两个方法,引发view变动则需要调用如下方法:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
-
过滤器,用于一些常见的文本格式化
-
一旦 data 数据变化就会重新解析模版。
-
create
-
mounted做初始化,把初始DOM放入页面时,仅调用一次。
- update
- destroy
-
-
-
创建
<template> </template> <script> import ComponentA from './ComponentA' import ComponentC from './ComponentC' export default { name: '' data(){ }, components: { ComponentA, ComponentC }, // ... } </script> <style> </style> 或者 const vc = Vue.extend({ })
-
注册
new Vue({ el: '#app', components: { 'component-a': ComponentA, 'component-b': ComponentB } })
-
使用
<div id="app"> <component-a></component-a> <component-b></component-b> <component-c></component-c> </div>
-
-
原型对象prototype
实例的隐式原型属性
__prototype__
永远指向类(父类)的原型对象
二、脚手架
国内代理备忘
npm init npm config set proxy http://10.101.1.1:8118 npm config set https-proxy http://10.101.1.1:8118 npm i -S vue
-
命令备忘
npm install -g @vue/cli vue --version vue create hello-world
修改配置,在
vue.config.js
中添加(参考配置)module.exports = { devServer: { host: '0.0.0.0', port: 80 } }
文件预览
-
render
vue.runtime.xxx.js 是运行版的vue,没有模版解析器,不能使用template配置项。
-
vue ref,vue用于获取元素,用来代替以前通过id获取元素的办法。
-
相当于后端中一个类class实例的只读属性,。
props: { title: String, likes: Number, isPublished: Boolean, commentIds: Array, author: Object, callback: Function, contactsPromise: Promise // or any other constructor }
-
有点类似于后端的基类和子类,mixin属于基类的方法,多个子类都可以使用基类的方法。
var vm = new Vue({ mixins: [mixin], methods: { ... } })
可以全局混入,也可以局部混入。
Vue.mixin({ xxx: xxx })
-
// 调用 `MyPlugin.install(Vue)` Vue.use(MyPlugin)
-
scope
解决style类名相同,引入顺序不同,导致冲突的问题。
<style scoped> </style>
不要在App里使用scoped
三、开发步骤(入门)
官网的一些内容和后端思维是互通的,比如状态提升
之类的,不要害怕,见人说人话,见鬼说鬼话。
-
拆分静态组件,按功能点拆分
-
静态组件,不考虑交互
- 把html整个塞到app里
- 把一个组件的html剪切后立马写占位符,然后塞到组件里。
- 把样式也拆了,塞到组件,style加个scoped。
-
数据交互设计
类似于后端,设计数据库和model。
组件通信的几种方式:
-
通过 props 存储数据,数据存在App上。
- 父组件定义data,通过props传给子组件(子要在props里接收)
- 父组件定义method,通过props传给另一个子组件
- 注意子组件的v-model不要改props
相当于内存存变量。
-
自定义事件
子组件给父组件传数据用。
在父组件中给子组件绑定自定义事件,事件回调在父组件。(父给子传函数,要求子返回数据)
-
全局事件总线
定义一个所有组件的子组件vc(也可以直接用vm)挂在prototype上,然后使用事件触发。相当于后端的redis。多读单写
-
消息订阅&发布
全局事件总线和消息订阅其实差不多的效果。多读单写
-
浏览器localstorage,相当于后端的数据库DB。
-
vuex
多读多写
-
-
动态组件
不同功能的增删查改+统计信息变动
- 查
- 增删,事件event。
- 改
四、高级
-
this.$emit('myEvent') <my-component v-on:my-event="doSomething"></my-component> this.$off('myEvent') this.$off(['myEvent','xxx']) this.$off()
也适用于子给父组件传输数据。
自定义事件由父组件运行,props方式由子组件运行。
还可以通过ref绑定自定义事件。
-
全局事件总线
定义一个所有组件的子组件vc,然后赋值给prototype。
改良版:
把vm当成所有组件的子组件,这样子?真是奇妙。
-
消息订阅&发布
$ npm -i pubsub.js import pubsub from 'pubsub-js' pubsub.publish('hello',666) pubsub.subscribe('hello',function(msgName,data){ }) pubsub.subscribe('hello',(msgName,data)=>{ })
-
nextTick
-
动画/过渡
其实就是封装了一个 transition 标签。
<transition name"xxx" appear></transition> .xxx-enter-active{ animation: kelu 1s linear; } .xxx-leave-active{} @keyframs kelu { from{ transform: xxx } to{} }
第三方动画:Animate.css:http://animate.style
-
跨域
axios请求,
cors(后端解决),代理服务器(前端解决)
module.exports = { devServer: { proxy: 'http://localhost:4000' } } module.exports = { devServer: { proxy: { '/api': { target: '<url>', ws: true, changeOrigin: true }, '/foo': { target: '<other_url>' } } } } # https://github.com/chimurai/http-proxy-middleware#proxycontext-config
-
<div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div> <base-layout> <template v-slot:header> <h1>Here might be a page title</h1> </template> <p>A paragraph for the main content.</p> <p>And another one.</p> <template v-slot:footer> <p>Here's some contact info</p> </template> </base-layout>
-
npm i vuex import Vuex from 'vuex' Vue.use(Vuex)
import { createApp } from 'vue' import { createStore } from 'vuex' // 创建一个新的 store 实例 const store = createStore({ state () { return { count: 0 } }, mutations: { increment (state) { state.count++ } } }) const app = createApp({ /* 根组件 */ }) // 将 store 实例作为插件安装 app.use(store)
dispatch -> commit ->
vc -> Action(ctx,value) -> mutation(state,value)
dispatch可以跳过,直接commit就行了。
mapState
// 在单独构建的版本中辅助函数为 Vuex.mapState import { mapState } from 'vuex' export default { // ... computed: mapState({ // 箭头函数可使代码更简练 count: state => state.count, // 传字符串参数 'count' 等同于 `state => state.count` countAlias: 'count', // 为了能够使用 `this` 获取局部状态,必须使用常规函数 countPlusLocalState (state) { return state.count + this.localCount } }) }
-
npm i vue-router@3 import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter)
-
一般组件
-
路由组件
<router-view></router-view> # 一级路由
- 多级路由
const routes = [ { path: '/user/:id', component: User, children: [ { path: 'profile', component: UserProfile, }, { path: 'posts', component: UserPosts, }, ], }, ]
-
路由传参
-
query参数
-
params参数
-
props(对象/函数/布尔)
解构赋值,连续解构赋值:
-
-
路由命名
-
路由历史记录
声明式 编程式 <router-link :to="..." replace>
router.replace(...)
<router-link :to="...">
router.push(...)
路由缓存:
<keep-alive :include="['xx','yy']"> <router-view></router-view> </keep-alive>
路由组件新增了两个生命周期
- activated
- deactivated
-
路由权限
数据存在meta里,
前置路由守卫,主要做鉴权
// GOOD router.beforeEach((to, from, next) => { if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) else next() })
后置路由守卫作用,一般就是修改页面内容之类的。
-
五、原理精进
-
为什么 vuejs 可以做到
reactive
交互式主要是通过
Object.defineProperty()
方法实现,参考https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty,直接在一个对象上定义一个新属性。自定义一个setter(所以必须用object类型)
六、其他
-
字面量赋值