Vue编写风格
- HTML元素的属性值始终带引号
<input type='text'>
;<AppSidebar :style={width:sidebarWidth+'px'}>
也是可以的(HTML中没有空格的属性值可以不用引号包裹)
Vue概念解释
单文件组件中 <style>
是可选的,<script>
和 <template>
至少要有一个。
一个理想的 Vue 应用是 prop 向下传递,事件向上传递的
html 使用双引号,js 使用单引号 DOM 模板使用短横线,其他的使用大驼峰
sourcemap
webpack/vue-cli 打包构建的资源到哪里去了 在浏览器dev-tools 中的 source 中查看
this.$nextTick 一旦你使用了
keep-alive
,那么你就可以访问另外两个生命周期钩子:activated
和deactivated
单文件组件
DOM模板 能被 html 识别的,以 html 形式编写
字符串模板 以字符串的形式写在选项对象的 template 属性上(全局注册和局部注册组件时)
JSX
html<!-- 在单文件组件、字符串模板和 JSX 中 --> <MyComponent/> <!-- 在 DOM 模板中, 没有自闭合, 不是匈牙利命名 --> <my-component></my-component>
响应式原理
Vue2的响应式是通过 Object.defineProperty 拦截数据,将数据(准确的说是对象属性)转换为 getter/setter 的形式,在访问属性时调用 getter函数,在修改属性时调用setter函数。然后利用发布-订阅(观察者模式)模式在数据变化时通知 Watcher 更新。
视图不会更新
通过索引修改数组的值
this.idArr[0] = {}
和this.idMap.list[0] = xxx
idMap 是一个对象。- 使用
arr.splice
设置或this.$set()
修改数组的
length
属性- 使用
arr.splice
设置或this.$set()
- 使用
数组有哪些方法支持响应式更新,如果不支持怎么办,底层原理如何实现?
- 支持:push、pop、shift、unshift、splice、sort、reverse,这些方法会改变原数组。
- 不支持:filter、concat、slice、forEach、map,这些方法不会改变原数组。可以修改整个数组实现响应式更新(将新的数组赋值给原来的数组)。
- 原理同样是使用 Object.defineProperty 对数组方法(get、set)进行改写
父组件通过 props
传递一个引用类型的值(propsObj
)给子组件,子组件将 propsObj
赋值给自身 data
属性。当子组件修改 initVal
时,父组件中的值也会被修改,因为父组件和子组件引用的用一个对象值。
props: ['propsObj'],
data() {
return {
initVal: this.propsObj
};
},
Vue 生命周期 watch 执行顺序 | 指令生命周期
初始渲染
- beforeCreate
- created
- beforeMount
- mounted
props 或 data 更新
- watch
- beforUpdate
- updated
watch immediate 情况下初始渲染
beforeCreate this 不是 undefined,不能访问当前实例上的属性,可以访问
Vue.prototype
上的属性data 函数 可以访问 this 及 props 上的属性,props 属性值是父组件执行 beforeMount 钩子时传入子组件的值
watch 监听器 可以访问
this
created
beforeMount
mounted
父组件和子组件的生命周期执行顺序
父组件的
beforeCreate
父组件的
created
父组件的
beforeMount
- 子组件的
beforeCreate
- 子组件的
created
- 子组件的
beforeMount
- 子组件的
mounted
- 子组件的
父组件的
mounted
指令生命周期
bind inserted update componentUpdated unbind
input 验证
Q: 非数据驱动,给 input 传入固定的值,input 框依然可以输入。
<input :value="text"/>
A: 使用 element.setAttirbute
不能设置 input 元素的值。使用 input.value = 'xx' 可以实现验证和长度限制。
异步更新DOM
vue观察到数据变化时并不是立即更新DOM,而是开启一个事件队列,缓冲在同一个事件循环中发生的所有数据改变,在缓冲时会去除重复数据。然后在下一个事件循环 tick 中刷新新队列并执行更新。
vue会根据当前浏览器环境优先使用原生 Promise
和 MutatinObserver
。
动态创建组件
动态创建组件有两种方式 1): new Vue
; 2): new Vue.extend(cmp)
单文件组件 export
就是组件选项对象
import Vue from 'vue';
import router from '@/router';
import CusDialog from './userDialog.vue';
export default function createDialog(title, fields) {
const instance = new Vue({
...CusDialog,
router,
propsData: {
visible: true,
title,
fieldOpts: fields,
},
}).$mount();
document.body.appendChild(instance.$el);
return instance;
}
// 用Vue.extend 继承 router、store 等,不用再次传递router
const extendCmp = Vue.extend(Cmp);
const renderCmp = (propsData) => {
const instance = new extendCmp({ propsData }).$mount();
document.body.appendChild(instance.$el);
return instance;
}
// 销毁
instance.$destroy();
instance.$el.remove();
$mount('#app')
组件会替换 #app
元素本身(outerHtml
)
获取插槽实例
this.$slots.default[0].componentInstance
内容分发
作用域插槽在需要同时封装逻辑、组合视图界面时很有用
动态组件
<component>
+<slot>
内容分发
表单组件包含了很多字段(input、radio、checkbox)等,在不同场景下使用表单组件,表单的逻辑是一致的,字段绑定、校验、提交,但是UI 布局会不同。
- cus-form.vue
<component :is="$slot.default">
<input v-modal="name" slot="name">
<checkbox v-modal="age" slot="age" />
</component>
- 使用
<cus-form>
<div>
<h1>年龄</h1>
<slot name="age"></slot>
</div>
</cus-form>
插槽透传
<template v-for="item in slots" #[item]="props">
<slot :name="item" v-bind="props"></slot>
</template>
<script>
const slots = Object.keys({
...this.$slots,
...this.$scopedSlots
});
</script>
程序化事件监听器
this.$on('event-name', handler);
this.$once('eventName', handler);
// 监听生命周期
this.$on('hook:beforeDestroy', handler);
问答
this.$emit的返回值是什么?
this.$emit的返回值就是this,如果需要在子组件中向父组件返回其他值,可以通过回调参数实现,即在子组件中this.$emit的实参中传递一个函数。
// 子组件内部
methods: {
handleChange(e) {
let callfn = val => {
console.log(val);
}
const res = this.$emit("change", e.target.value, callfn);
console.log(res, res === this);
}
}
// 父组件
const res = this.$emit("change", e.target.value, callfn);触发change事件,父组件中调用handleEventChange事件处理函数
<Event :name="name" @change="handleEventChange" />
handleEventChange(val, callback) {
// 形参val和callback分别由this.$emit()第二和第三个参数传递
this.name = val;
callback("hello");
return "hello";
}
相同名称的插槽(具名插槽)是合并还是替换?
- Vue2.5版本:普通插槽合并、作用域插槽替换。
- Vue2.6版本:都是替换。
数组有哪些方法支持响应式更新,如果不支持怎么办,底层原理如何实现?
- 支持:push、pop、shift、unshift、splice、sort、reverse,这些方法会改变原数组。
- 不支持:fiter、concat、slice,这些方法不会改变原数组;可以修改整个数组实现响应式更新(将新的数组赋值给原来的数组)。
- 原理同样是使用Object.defineProperty对数组方法进行改写
ajax请求
- ajax请求可以放在created和mounted生命周期中,但如果是做同构应用mounted不会在服务端调用,而created是会在服务端调用。
Vue-router
push
和 replace
的 onComplete 和 onAbort 回调参数,这些回调将会在导航成功完成 (在所有的异步钩子被解析之后) 或终止 (导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由) 的时候进行相应的调用。在 3.1.0+,可以省略第二个和第三个参数,此时如果支持 Promise,router.push 或 router.replace 将返回一个 Promise。
组件跳转到其他组件,触发 onComplete 回调
组件跳到自己,不带参数
jsthis.$router.push({ name: 'number'}, () => { console.log('组件2:onComplete回调'); }, () => { console.log('组件2,自我跳转:onAbort回调'); // 会执行 });
组件跳转到自己,带参数
jsthis.$router.push({ name: 'number', params: { foo: this.number}}, () => { console.log('组件2:onComplete回调'); // 不会执行 }, () => { console.log('组件2,自我跳转:onAbort回调'); // 不会执行 });
onComplete 和 onAbort 都不会执行,但是 beforeRouteUpdate 会执行。
Vue组件库
Vue.use的作用
Vue.use(mintui)做的就是注册所有的全局组件(webpack require.context),并在Vue.prototype添加一些属性,这样在组件内就可以使用this.xx
自定义插件,使用Vue.use()安装,如安装axios
// installer.js
jsfunction Installer(){} // 必须要有一个install属性 Installer.install = function(Vue){ // 1. 注册全局组件 Vue.component('xx',{ ... }); // 2. 添加属性 // Vue.protype.$log = function() { // console.log('hahaahhahaah') // } // this.$log = 'abxadksadas' 子类对象可以修改父类的属性 let log = function () { console.log('我们自己插件的log函数') } // 给原型定义属性的获取和设置,设置:见鬼去吧,获取就给你 Object.defineProperty(Vue.prototype,'$log',{ // 设置 $log属性时的行为 || 不给,不能设置 set:function (newV) { console.log('你做梦'); // log = newV; }, get:function () { // 获取方式 return log; } }) }
Vue单页面SEO优化
- 服务端渲染 SSR :Nuxt.js
- 页面预渲染
- 在页面中先预渲染部分静态内容,不用JS注入
- 使用插件:vue-cli-plugin-prerender-spa 或 prerender-spa-plugin
预渲染
单页面(SPA) 应用的 SEO 优化有服务端渲染(SSR) & 页面预渲染两种方法。
- 预渲染的使用场景更多是简单的静态页面,加快页面的加载速度,并且侵入性更小,在已上线的项目稍加改动也可以轻松引入预渲染机制。
- 服务端渲染适用于复杂、较大型、与服务端交互频繁的功能型网站,比如电商网站。SSR方案则需要将整个项目结构推翻。
- 两则的区别在于渲染的时机不同:
prerender-spa-plugin
是在打包过程中渲染,注定了其只能渲染静态路由,而prerender
是在请求时渲染,所以可以渲染动态的路由。
vue 预渲染实现
插件配置
prerender-spa-plugin & vue-meta-info
// vue.config.js
// 这三项一定要有,因为下面configureWebpack中用到了
const PrerenderSPAPlugin = require('prerender-spa-plugin');
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;
const path = require('path');
module.exports = {
// 预渲染关键配置
configureWebpack: () => {
if (process.env.NODE_ENV !== 'production') return;
return {
plugins: [
new PrerenderSPAPlugin({
// 生成文件的路径,也可以与webpakc打包的一致。
// 下面这句话非常重要!!!
// 这个目录只能有一级,如果目录层次大于一级,在生成的时候不会有任何错误提示,在预渲染的时候只会卡着不动。
staticDir: path.join(__dirname, 'dist'),
// 对应自己的路由文件,如果有参数需要写具体参数,比如/a/:id需要写/a/123456
routes: ['/', '/about'],
// 这个很重要,如果没有配置这段,也不会进行预编译
renderer: new Renderer({
inject: {
foo: 'bar'
},
headless: false,
// 在 main.js 中 document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。
renderAfterDocumentEvent: 'render-event'
})
})
]
};
}
};
// main.js
new Vue({
router,
store,
render: (h) => h(App),
// 添加mounted,不然不会执行预编译
mounted() {
document.dispatchEvent(new Event('render-event'));
}
}).$mount('#app');
服务端配置
History 模式需要后台配置支持,最简单的是通过 nginx 配置 try_files 指令。
vue-meta-info
组件内使用
<script>
export default {
metaInfo: {
title: 'My Example App', // set a title
meta: [{ // set meta
name: 'keyWords',
content: 'My Example App'
}]
link: [{ // set link
rel: 'asstes',
href: 'https://assets-cdn.github.com/'
}]
}
}
</script>
用作鉴权的函数式组件
Authorized.vue 鉴权组件
<script>
import { check } from "../utils/auth";
export default {
functional: true,
props: {
authority: {
type: Array,
required: true
}
},
render(h, context) {
const { props, scopedSlots } = context;
// check 用来判断是否授权
return check(props.authority) ? scopedSlots.default() : null;
}
};
</script>
- main.js 注册
import Authorized from "Authorized.vue";
Vue.component("Authorized", Authorized);
- 使用
<Authorized :authority="['admin']">
<SettingDrawer />
</Authorized>
// 如果传递的 authority 通过 check, 则 <SettingDdrawer> 组件会显示, 否则不会显示;
权限指令
指令 direction.js
import { check } from "../utils/auth";
function install(Vue, options = {}) {
Vue.directive(options.name || "auth", {
inserted(el, binding) {
if (!check(binding.value)) {
el.parentNode && el.parentNode.removeChild(el);
}
}
});
}
export default { install };
- 注册指令
import Auth from "direction.js";
Vue.use(Auth);
- 使用指令
<comp v-auth="['admin']" />
JSX 使用
@vue/cli-plugin-babel/preset
module.exports = {
presets: ['@vue/cli-plugin-babel/preset'],
};
Vue源码
- 阅读源码哈https://github.com/vueComponent/ant-design-vue vue源码可以推荐两个: https://ustbhuangyi.github.io/vue-analysis/https://github.com/answershuto/learnVue
其他
$nextTick
是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用$nextTick
,则可以在回调中获取更新后的 DOM;