VUE-CLI
1.创建项目
// 1.需要先删除旧版本cli
npm uninstall vue-cli -g
// 2.安装@vue/cli
npm install @vue/cli -g
// 3.查看vue版本
vue --version
// 4.创建vue项目
vue create ym001
参考链接 https://www.jianshu.com/p/7cccc5b38dcc
参考链接 https://www.cnblogs.com/coober/p/10875647.html
2. 性能优化
https://www.yuque.com/fe9/basic/locx8c#728b4ded
Vue
注意事项:
vue组件样式添加了scoped 最外层的元素会被父组件样式穿透
// index.vue
<template>
<div class="container">
<page-header />
<div class='content'>我的背景是红色的</div>
</div>
</template>
<script>
import PageHeader from '@/components/setting/page-header'
export default {
components: { PageHeader }
}
</script>
<style lang="scss" scoped>
.container{
position: fixed;
height: 100%;
width: 100%;
.content {
background: red;
}
}
</style>
// page-header.vue
<template>
<div class="content">
我的背景也是红色的
</div>
</template>
...script ...style:scoped
Object.defineproperty / proxy
- 两者最大的区别在于proxy能监听对象的所有属性,
defineproperty只能监听对象的某一个属性 ```javascript // Object.defineProperty// 不兼容IE8 var obj = {}; // obj.name = 666 // Object.defineProperty(obj,’name’,{ // value:666, // enumerable:false,// 是否可枚举, 是否能够循环的到 默认false // // writable:false, // 是否可以更改 默认不可改写 // // configurable:false // 是都可删除 delete 默认不可删除 // }); var t=666; Object.defineProperty(obj,’name’,{ // 数据劫持 get(){ //当name被调用的时候 会触发 这个 get;调用时 获取到的值 是 这个函数的返回值 console.log(‘get’) return t }, set(val){ // 当name属性被赋值的时候 会触发 这个set val对应的就是给name赋予的哪个值 console.log(‘set’,val) t = val; } });
// Proxy var obj2 = { name:123, age:456, sex:7777, a:{ qqq:123 } } var tempObj2 = new Proxy(obj2,{ get(target,key){ // target就是 obj2 // key 就是我们调用的对应的属性的名字 console.log(‘get’,arguments) return target[key] }, set(target,key,val){ console.log(‘set’) target[key] = val } }); // Proxy 优势 不用挨个属性进行劫持
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
<a name="XqSar"></a>
### 自定义指令 directive
<a name="dMgp3"></a>
### mvvm v-model 原理
```javascript
class myVue {
constructor(options) {
this.$el = document.querySelector(options.el)
this.$data = options.data
this.observe(this.$data) // 负责数据劫持
this.nodeToFragment(this.$el, this)// 负责模板编译
}
observe(data) {
// 把data中的属性进行劫持
if (({}).toString.call(data).match(/ (\w+)]/)[1] != 'Object') return
let keys = Object.keys(data)// 获取到了每一个属性名
keys.forEach(key => {
this.defineReactive(data, key, data[key])
})
}
defineReactive(obj, key, value) {
// 负责使用 defineProperty进行数据劫持
this.observe(value)// 递归劫持
let dep = new Dep()
Object.defineProperty(obj, key, {
get() {
console.log('get')
if (Dep.target) {
dep.addSub(Dep.target)
}
return value
},
set(newVal) {
console.log('set')
if (value !== newVal) {
value = newVal
dep.notify()
}
}
})
}
nodeToFragment(el, vm) {
// 把节点转到 fragment上
let fragment = document.createDocumentFragment()
let child
while (child = el.firstChild) {
fragment.appendChild(child)
this.compile(child, vm)
}
el.appendChild(fragment)
}
compile(node, vm) {
// 编译 node中的 vue语法
// 先判断 node的节点类型 看他是一个 元素节点 还是 文本节点
if (node.nodeType == 1) {
// 元素节点 // 要获取行内属性的
let attrs = node.attributes;
[...attrs].forEach(item => {
// 查看当前的属性名是不是 v-xxx的
if (/^v-/.test(item.name)) {
// 证明这个属性 是 v-model 我i们要获取 后边的name这个字符
let vName = item.nodeValue // name
let val = vm.$data[vName]// 珠峰
new Watcher(node, vName, vm)
node.value = val// 把珠峰这两字 放到 input框中
node.addEventListener('input', (e) => {
vm.$data[vName] = e.target.value
})
}
});
// 处理当前元素的子元素
[...node.childNodes].forEach(item => {
this.compile(item, vm)
})
} else {
// 文本节点
let str = node.textContent// 获取文本字符串
// console.log(str)// {{name}}
if (/\{\{(\w+)\}\}/.test(str)) {
console.log(str)
str = str.replace(/\{\{(\w+)\}\}/, (a, b) => {
new Watcher(node, b, vm)
return vm.$data[b]
})
node.textContent = str
}
}
}
}
//订阅器
class Dep {
constructor() {
this.subs = []
}
addSub(sub) {
this.subs.push(sub)
}
notify() {
this.subs.forEach(sub => {
// debugger
// 让对应的事件执行 sub 就是哪些 watcher
sub.update()
})
}
}
// 订阅者
class Watcher {
constructor(node, key, vm) {
Dep.target = this
this.node = node
this.key = key
this.vm = vm
this.getValue()
Dep.target = null
}
update() {
this.getValue()
// debugger
if (this.node.nodeType == 1) {
// 就是input框
this.node.value = this.value
} else {
this.node.textContent = this.value
}
}
getValue() {
// 获取新值
this.value = this.vm.$data[this.key]
}
}
vue.use
传入实参会默认执行某些方法, 会根据实参的类型进行不同操作
判断传入实参是否有install方法,并调用,若install方法不存在
- 再判断实参是不是一个函数,若是执行实参函数,若不是将不在往下执行
$nextTick原理
- nextTick内部有一个callbacks事件池, 还有一个pedding的状态
- 预先会进行浏览器能力检测, promies > setImmediate > MessageChannel > setTimeout
- 当用户调用nextTick方法, 往callbacks push箭头函数
- 若传入cb, cb.call执行,否则走promies微任务执行
- 判断pedding为false 改变状态调用timeFun方案挨个执行事件池方法
- 执行完状态改为false
provide/inject使用.父组件定义provide 子组件使用inject定义 泼歪德 in杰可特
https://juejin.im/post/5dc4cbeff265da4d1f51c7f1
$emit(父组件事件名,参数) 父组件使用@事件名= 方法 驼峰命名
动态组件 数据存放 不建议在组件中直接更改state的数据,因为这样不能被vue-devtools监控,
类似计算属性 我将getters属性理解为所有组件的computed属性, 也就是计算属性. vuex的官方文档也是说到可以将getter理解为store的计算属性, getters的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
```javascript
getters:{
//state是vuex的对象
nameInfo(state){ },
//getters用于将getters下的其他getter拿来用
fullInfo(state,getters){ } //在组件中使用getters
this.$store.getters.fullInfo const store = new Vuex.Store({
modules: {
moduleA,
moduleB
}
})
store.state.moduleA // -> moduleA 的状态
store.state.moduleB // -> moduleB 的状态 $router.push() 会在history中添加一条记录 还有值得提及的,Vue Router 的导航方法 (push、 replace、 go) 在各类路由模式 (history、 hash 和 abstract) 下表现一致。
重定向”的意思是: 文档概念:正如其名,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。 记住参数或查询的改变并不会触发进入/离开的导航守卫。你可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。 我的理解:路由守卫像是一个安检过程,通过才能进去路由,不通过禁止,中间件做判断
理解:可通过路由守卫的to from 访问到啊 meta的信息 按需打包 采用vue异步组件+webpack代码分割功能 编写思路, 利用vue 全局mixins注入$router元信息
上述内容可以通过 Vue 的
动态更换组件
props
数组传,对象传,prop验证
https://cn.vuejs.org/v2/guide/components-props.html
禁用 Attribute 继承
inheritAttrs: false, 默认true
$listeners
.sync
https://www.jianshu.com/p/6b062af8cf01
插槽
组件里面必须有
插槽作用域 如果插槽中要使用data的数据
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
具名插槽 可以在插槽上定义v-slot=”mySlot”
注意 v-slot 只能添加在 上 (只有一种例外情况),这一点和已经废弃的 slot attribute 不同。
VueX
State
Getters
return "姓名:"+state.name
return getters.nameInfo+'年龄:'+state.age
}<a name="TiTuS"></a>
### Mutations
读音:谬忒顺
1. 操作state数据的中间件
1. 我将mutaions理解为store中的methods,
1. mutations对象中保存着更改数据的回调函数,该函数名官方规定叫type, .//
不允许status[xxx]修改对象,需要重新赋值
```javascript
//vueX文件
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.store({
state:{
name:'helloVueX'
},
mutations:{
//es6语法,等同edit:funcion(){...}
edit(state,param){
state.name = 'jack'
}
}
})
export default store
//组件使用 在组件methods中调用
//第一个参数是state, 第二参数是payload, 也就是自定义的参数
//优先使用
this.$store.commit('edit',{age:15,sex:'男'});
//对象传参写法
this.$store.commit({
type:'edit',
payload:{
age:15,
sex:'男'
}
})
Actions
actions:{
aEdit(context,payload){
setTimeout(()=>{
context.commit('edit',payload)
},2000)
},
//由于是异步操作,所以我们可以为异步操作封装一个promise对象
aEdit(context,payload){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
context.commit('edit',payload)
resolve()
},2000)
})
}
}
this.$store.dispatch('aEdit',{name:'杨明',age:27})
modules
<a name="oUXn2"></a>
### vuex重写
代码核心:
1. Vue.use() install ==> vue.mixin, beforeCreate生命周期,
1. constructor ==> new Vue()
1. 初始化mutations, getters, actions
1. 辅助函数
编写思路:
1. vue.use 初始化install 方法, 该方法利用vue.mixins全局混入一个breforeCreate声明周期函数, 将$store执行前注入到根/子孙组件上
1. 当 new Store类时,创建一个vue实例把参数的store存放在vue.data上,实现双向数据绑定(数据劫持)
1. 把mutations, getters, actions 挨个循环存放在store实例上
1. 编写私有方法commit处理函数 commit必须在this.实例上且需要使用箭头函数,保证this指向
1. 编写dispatch方法=> 调用actions[type]执行传参
辅助函数: 循环创建key对应的fun<br />项目代码: [github/myVuex](https://github.com/Ym-wei/myVuex/blob/master/src/myVuex/index.js)
<a name="j4r1o"></a>
## Vue-router
<a name="6R5fO"></a>
### 路由传参
通过:传参<br />/user/**:**name/**:**age 访问$route.params.name || $route.params.age<br />通过?传参<br />/user?name=杨明 访问$route.query.name<br />匹配路由<br /> path:/user**-* **<br />this.$router.push('/user-admin') 获取:this.$route.params.pathMatch //admin<br />path:*<br />this.$router.push('/user-admin') 获取:this.$route.params.pathMatch //user-admin<br />匹配优先级<br />谁先定义的谁优先级最高.
<a name="y6REb"></a>
### 嵌套路由(子路由) TODO
定义:children [{path,component,name...}]<br />使用 <router-view><router-view/>
<a name="oTXzY"></a>
### 编程式的导航 js跳转路由
注意:如果提供了 path,params 会被忽略,上述例子中的 query 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name 或手写完整的带有参数的 path:
```javascript
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
//完整
this.$router.push({
name:'定义的路由名称',
params:{}
query:{}
})
$router.replace 不会添加history记录,会替换当前的记录
$router.go() //传入一个整数 类似window.history.go()方法// 在浏览器记录中前进一步,等同于 history.forward()
this.$router.go(1)
// 后退一步记录,等同于 history.back()
this.$router.go(-1)
// 前进 3 步记录
this.$router.go(3)
// 如果 history 记录不够用,那就默默地失败呗
this.$router.go(-100)
this.$router.go(100)
命名视图
<router-view name=a></router-view> 给视图定义名字
<router-view name=b></router-view>
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
重定向 redirect (re dir ect)
{ path: '/a', redirect: '/b' }
当用户访问 /a时,URL 将会被替换成 /b,然后匹配路由为 /b,那么“别名”又是什么呢?
理解为通过一个中转,不想在地址栏看到真实的路由/做些事情
导航守卫 导航钩子 访问拦截 中间件
全局守卫
//访问之间
router.beforeEach((to,from,next)=>{
//...
to//路由信息
from //之前的路由
next(false)//不允许操作
})
//访问之后
router.afterEach((to,from)=>{
})
路由独享守卫
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
//前
beforeEnter: (to, from, next) => {
// ...
}
//后
afterEach:(to,from)=>{
//...
}
}
]
})
组件守卫 少用
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
HTML5 History模式 mode:’history’
const router = new VueRouter({
mode: 'history',//启用history模式
routes: [...]
})
路由元信息 meta 自定义路由信息
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
children: [
{
path: 'bar',
component: Bar,
// a meta field 自定义路由信息
meta: { requiresAuth: true,//... }
}
]
}
]
})
//访问
//to.matched.some(record => record.meta.xxx
路由懒加载
vue-router重写
通过hashchange事件监听路由哈希变化,
通过匹配路由哈希与组件路由配合进行渲染