Vue
MVVM框架
数据请求: created、mounted
vue created app名称
vue动态参数
<a @[event]=“doSomething”> … 里面不能包括空格引号
计算属性 computed
不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值,提高性能
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
计算属性默认只有一个getter,可以有setter
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
侦听属性 watch
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
不想复用template元素时,使用key属性
这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key attribute
指令之v-if跟v-show
一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好
注意
在文本区域插值 (<textarea>{{text}}</textarea>) 并不会生效,应用 v-model 来代替
组件切换
<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>
dom模板使用注意事项 使用is解决 表示渲染成什么组件
渲染报错
<table>
<blog-post-row></blog-post-row>
</table>
改为:
<table>
<tr is="blog-post-row"></tr>
</table>
组件名称注意
1:Vue.component('my-component-name', { /* ... */ })
使用: <my-component-name>
2: Vue.component('MyComponentName', { /* ... */ })
使用: <my-component-name>或<MyComponentName> 建议使用分割符命名小写
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符
因此组件传值使用时
Vue.component('blog-post', {
// 在 JavaScript 中是 camelCase 的
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})
<blog-post post-title="hello!"></blog-post>
props类型检查
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
prop传值 父组件想子组件传递的任何至都要prop声明
如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)
<blog-post v-bind="post"></blog-post>
事件名称使用 kebab-case 的事件名 就是用-连接起来的小写
slot缩写: v-slot:header 简写为:#header 该缩写只在其有参数的时候才可用。
异步组件 按需加载 提高性能
局部异步组件
new Vue({
// ...
components: {
'my-component': () => import('./my-async-component')
}
})
Vue.component(
'async-webpack-example',
// 这个 `import` 函数会返回一个 `Promise` 对象。
() => import('./my-async-component')
)
vue组件
vue组件创建有3中方法 使用template标签,使用script标签
Vue.extend({
template: ""
})
this.$parent.方法名称(),不需要传递方法名称 不好用 this.$parent.value可以访问父组件的值
父组件给子组件添加ref,然后父组件可以调用子组件的方法 用的比较多 父组件调用子组件的方法
this.$emit需要传递父组件的方法名称,不需要props中声明,只有传值才需要声明
this.$on()跟this.$emit()是一对,必须在同一个组件使用,箭头emit()
自定义指令 响应式原理 ajax router(路由守卫)
var bus = new Vue()的使用 o n ( ) 与 on()与 on()与emit()
.sync修饰符 使得props变成双向数据流,子组件可以修改父组件props,父组件可以修改props
父组件使用子组件
<HelloWorld :msg.sync="msg" />
子组件使用$emit()
changeParentValue(){
console.log("修改值")
this.$emit("update:msg","子组件修改父组件的props值")
}
问题: prop传值命名跟传函数命名
组件通信使用方式
props、$emit
/$on
、vuex、$parent
/ $children
、$attrs
/$listeners
和provide/inject
$listeners可以让你在孙子组件改变父组件的值
这个{{$attrs}}的值是父组件中传递下来的props(除了子组件childcom组件中props声明的)。
inheritAttrs默认为true:表示未被子组件声明的props会被当做普通html属性显示,如果为false,可以在this. a t t r s 访 问 到 未 被 声 明 的 p r o p s 并 可 以 向 子 组 件 传 递 ; attrs访问到未被声明的props并可以向子组件传递; attrs访问到未被声明的props并可以向子组件传递;listeners可以传递爷爷组件的方法 只要在子组件使用的孙子组件中v-on=" l i s t e n e r s " , 孙 子 组 件 就 可 已 访 问 , 同 样 爷 爷 组 件 必 须 绑 定 事 件 , 孙 子 组 件 通 过 调 用 t h i s . listeners",孙子组件就可已访问,同样爷爷组件必须绑定事件,孙子组件通过调用this. listeners",孙子组件就可已访问,同样爷爷组件必须绑定事件,孙子组件通过调用this.emit()使用
e m i t / emit/ emit/on是总线方式 是兄弟组件 其他有待测试 回用于同一个组件内使用
vue路由参数传递
-
使用params传递 当在routes中设置props:ture时,我们在组件中可以通过 props: [‘id’]获取路由中的参数(id参数)值
当props:false是无法获取的。如果我们不使用props属性,只能使用传统的方式为{{ $route.params.id }},组件就和路由耦合了 -
props为true,通过props为函数时进行传值,此时用的query方式进行参数的传递,可以在组件中使用props: [‘name’, ‘age’]接收
{ path:"zdh", component:Zdh, props : (route) => ({ name : route.query.name, age : route.query.age }) }
若props为false,只能会用传统的this.$route.query.参数
-
使用默认方式 props默认为false,不需要写 全使用传统方式获取路由参数
vue provide跟inject传值 直系传值
-
provide: { //这2种做法不是响应式的,即父组件Provide值改变不会影响后续子孙值的改变 names: '陈姐', country: '美国' } provide() { return { names: '陈姐', country: '美国' } },
-
provide() {//这种方法是响应式的, 父组件names改变,会影响到子孙组件 this.names = Vue.observable({ names: '陈姐' }) return { names: this.names, country: '美国' } }, methods中 changeInject(){ this.names.names = "中国" }
-
vue.observe({})
让一个对象可响应。Vue 内部会用它来处理 data
函数返回的对象。
v-once指令 将组件放在内存中,提高性能,如果组件包含大量静态内容,就可以使用
vue传值与组件命名规范
-
传值使用小写中间加- 比如 data-name, props接收使用dataName接收
-
传方法原来怎样还是怎样使用
-
全局组件命名:名称使用中间添加-形式,使用组件时跟名称一样添加-
-
外部引用组件命名使用驼峰法,使用时转小写加-
总结: 这些只是一种命名规范,并不是强制要求,使用驼峰命名也可以,不影响使用,主要是html对大小写不敏感,大写会转换成小写
nextTick()使用
this.$nextTick()将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新;
在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作并无作用,而在created()里使用this.$nextTick()可以等待dom生成以后再来获取dom对象
需要注意的是,在 created 和 mounted 阶段,如果需要操作渲染后的试图,也要使用 nextTick 方法。
例一:不适用nexttick获取不到dom元素
template>
<div>
<h1>tick page</h1>
<p ref="p">{{ level }}</p>
</div>
</template>
<script>
export default {
data() {
return {
level: "grade"
};
},
created() {
this.$nextTick(() => {
let p = this.$refs.p;
console.log("created");
console.log(p.innerText);
});
},
mounted() {
this.$nextTick(() => {
let p = this.$refs.p;
console.log("mounted");
console.log(p.innerText);
});
}
};
</script>
例2:如果不使用nexttick获取到的width为0
<template>
<div>
<h1>tick page</h1>
<div class="container" v-show="flag" ref="divs"></div>
<button @click="show">切换</button>
<div v-show="flag">{{ width }}</div>
</div>
</template>
<script>
export default {
data() {
return {
level: "grade",
flag: false,
width: ""
};
},
methods: {
show() {
this.flag = true;
this.$nextTick(() => {
this.width = this.$refs.divs.offsetWidth;
console.log('width:'+this.width)
});
}
}
};
</script>
mixin 混入:使用公共方法,避免代码污染
Vue.mixin(mixin) 全局混入,都可以使用,先执行混入,在执行组件本身
组件混入 mixins: [mixin];名称若是跟引用的组件相同,则使用的是组件本身的值或方法
vue响应式原理
Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
const object1 = {};
Object.defineProperty(object1, 'property1', {
value: 42,
writable: false
});
console.log(object1.property1);
对象的
function render () {
console.log('模拟视图渲染')
}
let data = {
name: '浪里行舟',
location: { x: 100, y: 100 }
}
observe(data)
function observe (obj) { // 我们来用它使对象变成可观察的
// 判断类型
if (!obj || typeof obj !== 'object') {
return
}
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key])
})
function defineReactive (obj, key, value) {
// 递归子属性
observe(value)
Object.defineProperty(obj, key, {
enumerable: true, //可枚举(可以遍历)
configurable: true, //可配置(比如可以删除)
get: function reactiveGetter () {
console.log('get', value) // 监听
return value
},
set: function reactiveSetter (newVal) {
observe(newVal) //如果赋值是一个对象,也要递归子属性
if (newVal !== value) {
console.log('set', newVal) // 监听
render()
value = newVal
}
}
})
}
}
数组的:
function render() {
console.log('模拟视图渲染')
}
let obj = [1, 2, 3]
let methods = ['pop', 'shift', 'unshift', 'sort', 'reverse', 'splice', 'push']
// 先获取到原来的原型上的方法
let arrayProto = Array.prototype
// 创建一个自己的原型 并且重写methods这些方法
let proto = Object.create(arrayProto)
methods.forEach(method => {
proto[method] = function() {
// AOP
arrayProto[method].call(this, ...arguments)
render()
}
})
function observer(obj) {
// 把所有的属性定义成set/get的方式
if (Array.isArray(obj)) {
obj.__proto__ = proto
return
}
if (typeof obj == 'object') {
for (let key in obj) {
defineReactive(obj, key, obj[key])
}
}
}
function defineReactive(data, key, value) {
observer(value)
Object.defineProperty(data, key, {
get() {
return value
},
set(newValue) {
observer(newValue)
if (newValue !== value) {
render()
value = newValue
}
}
})
}
observer(obj)
function $set(data, key, value) {
defineReactive(data, key, value)
}
obj.push(123, 55)
console.log(obj) //[1, 2, 3, 123, 55]
Vue.set() 只能设置对象或数组(测试) 可以添加属性或修改属性
问题:区别
- 数组与对象
this.$set(Array, index, newValue)
this.$set(Object, key, value)
Vue.set使用
<template>
<div>
<p>{{ user }}</p>
<ul>
<li v-for="item in list" :key="item.id">
{{ item.id }} === {{ item.name }}
</li>
</ul>
<button @click="setData">动态赋值</button>
<button @click="changeData">动态改值</button>
</div>
</template>
<script>
import Vue from 'vue'
export default {
data() {
return {
user: {},
list: [
{ id: 1, name: "西施" },
{ id: 2, name: "吴宗宪" },
{ id: 3, name: "奥巴马" }
]
};
},
methods: {
setData() {
Vue.set(this.user, "sex", "女");
Vue.set(this.list, this.list.length, { id: 10, name: "中uoi1" });
},
changeData() {
Vue.set(this.list, 0, { id: 5, name: "吴晓辉" });
}
}
};
</script>
this.$set使用 跟Vue.set使用方式一样
methods: {
setData() {
this.$set(this.user, "sex", "女");
this.$set(this.list, this.list.length, { id: 10, name: "中uoi1" });
},
changeData() {
this.$set(this.list, 0, { id: 5, name: "吴晓辉" });
}
}
PS: 对于一般的对象新增属性,只需要对象新增属性赋值操作就可以啦,但是不会触发视图更新.
//不能更新视图
下面这个可以更新视图 主要是使用了this.user.hobby = "banan"的原因 去掉就没反应
change() {
console.log('啊啊啊')
this.user.hobby = "banan"
this.user.like = 'mountain'
console.log(this.user)
},
更新表格
Object.assign() 合并对象 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。动态修改视图
可以使用Object.assign() 来添加新的属性,但是要创建 一个新的对象。这样才能触发视图更新?(需要测试)
this.data = Object.assign({}, this.data, { age:‘lisi’ }) //这里{} 就是创建的新的对象
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }
console.log(source)
// Object { b: 4, c: 5 }
console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }
触发vue响应式:pop() shift() unshift() splice() sort() reverse() push()
dom更新是异步的
vm.$forceUpdate() 强制更新当前组件:影响当前实力本身和插槽内的内容
如果一个watcher被多次触发,只会推入到队列中一次
事件循环: tick
Vue.delete() 别名是vue.$delete()
删除对象的属性 ,触发视图更新,使用js方法不会更新视图
data : {
namelist : {
id : 1,
name : '叶落森'
}
}
删除对象属性
// 删除name
delete this.namelist.name;//js方法 不能更新视图
Vue.delete(this.namelist,'name');//vue方法 可以更新视图
通过this.$options.data()获取该组件初始状态下的data
通过this.$data获取当前状态下的data
Object.assign(this. d a t a , t h i s . data, this. data,this.options.data())就可以将当前状态的data重置为初始状态。当属性名相同时,新增的会覆盖原来的属性
Object.assign可以用来处理数组,但是会把数组视为对象。Object.assign([1, 2, 3], [4, 5]) // [4, 5, 3]
- PS: 在需要改变整个数组的值的时候,使用Object.assign和$set并不能有很好的体验效果(新增的是键值),所以可以使用push、filter、等方法改变原数组值; this.obj可能是父级组件传过来的prop,而为了遵循 单向数据流 的设计理念,不直接修改该数据对象,而是生成一个新的数据对象
- this.obj = Object.assign({}, this.obj, {k: v})(推荐写法) 表达式右侧会生成一个新的对象,this.obj会指向一个新的引用地址(常用于 浅拷贝 对象)
- this.obj = Object.assign(this.obj, {k: v}) 表达式右侧只会修改this.obj,this.obj仍然指向原引用地址(常用于 合并 对象)
<template>
<div class="home">
<p>vue object.assign()</p>
<p>{{ message }}</p>
<p v-for="(value, key) in user" :key="key">{{ value }} === {{ key }}</p>
<button @click="change">修改数据</button>
<button @click="add">添加数据</button>
</div>
</template>
<script>
export default {
name: "Home",
data() {
return {
message: "哈哈哈哈",
user: {
name: "Lily",
age: 12
}
};
},
methods: {
change() {
console.log('修改数据')
Object.assign(this.user, { name: 'peony' });
},
add(){
console.log('添加数据')
this.user = Object.assign({}, this.user, {city: '深圳'})
console.log(this.user)
}
}
};
</script>
props默认值: 下面这两种效果一样,选择一种就可以 只能使用default
prop: {
name: {
default: '1',
default: () => '1'
}
}
Bus总线测试
Vue.prototype.bus = new Vue();
Bus.vue:
<template>
<div>
<p>bus总线测试 {{msg}}</p>
<MBusItem></MBusItem>
</div>
</template>
<script>
// import Vue from 'vue'
import MBusItem from '../components/BusItem'
export default {
data() {
return {
msg: ''
}
},
methods: {
},
created() {
this.bus.$on('getMessage', (value) => {
console.log(value)
console.log('hahhaha')
this.msg = value
})
},
components: {
MBusItem
}
}
</script>
BusItem.vue:
<template>
<div>
<p>bus item</p>
<p>嘎嘎嘎嘎</p>
<button @click="getBus">bus测试</button>
</div>
</template>
<script>
// import Vue from 'vue'
export default {
data() {
return {}
},
methods: {
getBus(){
this.bus.$emit('getMessage','发送过去的值')
}
}
}
</script>
vue生命周期 刷面试题
vue.extend() extends 跟混入传差不多,但是区别???
Extend.vue
<template>
<div>
<p>extends {{ msg }}</p>
<button @click="extend"> 调用extend</button>
</div>
</template>
<script>
import extend from '../components/extend'
export default {
data() {
return {};
},
methods: {
extend() {
this.add();
}
},
extends: extend
};
</script>
extend.js:
export default {
created() {
console.log('extend 声明周期')
},
methods: {
add(){
console.log('extend++')
}
},
data() {
return {
msg: '哈哈哈哈 我是extend'
}
},
}
1、Vue.mixin即是混入。mixin的作用是多个组件可以共享数据和方法,在使用mixin的组件中引入后,mixin中的方法和属性也就并入到该组件中,可以直接使用,在已有的组件数据和方法进行了扩充。Vue.mixin()可以把你创建的自定义方法混入所有的 Vue 实例
2、mixins的特点:方法和参数在各组件中不共享
3、extends是一个选项,主要是为了便于扩展单文件组件。只不过接收的参数是简单的选项对象或构造函数,所以extends只能单次扩展一个组件
4、Vue.extend是一个全局的API,实际是创建一个构造器,并将其挂载到HTML的元素上(创建一个template标签)。可以通过propsData传参。
<div id="transmit"></div>
var Profile3 = Vue.extend({
template: '<p>{{extendData}}</br>实例传入的数据为:{{propsExtend}}</p>',
data: function () {
return {
extendData: '这是extend扩展的数据',
}
},
props:['propsExtend']
})
// 创建 Profile 实例,并挂载到一个元素上。可以通过propsData传参.
new Profile3({propsData:{propsExtend:'我是实例传入的数据'}}).$mount('#transmit')
.native修饰符监听子组件的原生事件(有限制) $options
Native.vue:
<template>
<div>
<p>native 修饰符的使用</p>
<NativeItem @input.native="onChange"></NativeItem>
</div>
</template>
<script>
import NativeItem from "../components/NativeItem";
export default {
data() {
return {
msg: "哈哈哈哈"
};
},
methods: {
onChange(el){
console.log('哈哈哈哈')
console.log(el.target.value)
}
},
components: {
NativeItem
}
};
</script>
NativeItem.vue:
<template>
<input type="text" />
</template>
Vue2-filters插件:过滤器
function format (num) {
var reg=/\d{1,3}(?=(\d{3})+$)/g;
return (num + '').replace(reg, '$&,');
}
1、正则表达式 \d{1,3}(?=(\d{3})+$) 表示前面有1~3个数字,后面的至少由一组3个数字结尾
2、?=表示正向引用,可以作为匹配的条件,但匹配到的内容不获取,并且作为下一次查询的开始
3、$& 表示与正则表达式相匹配的内容,具体的可查看 w3school的replace()方法
H5 worker多线程 跟 blob传输文件
node fs跟fse模块
在导航完成前获取数据 getPost()需要自己写
filter格式化千分位例子
-
function format(num) { var arr = [], str = num + '', count = str.length; while (count >= 3) { arr.unshift(str.slice(count - 3, count)); count -= 3; } // 如果是不是3的倍数就另外追加到上去 str.length % 3 && arr.unshift(str.slice(0, str.length % 3)); return arr.toString(); }
-
function format(num) { var str = num+''; // ["8", "7", "6", "5", "4", "3", "2", "1"] return str.split("").reverse().reduce((prev, next, index) => { return ((index % 3) ? next : (next + ',')) + prev; }) }console.log(format(12345678));
-
function format (num) { var reg=/\d{1,3}(?=(\d{3})+$)/g; return (num + '').replace(reg, '$&,'); }
vuerouter 命名视图
const routes = [
{
path: "/",
name: "Home",
component: Home,
children: [
{
path: '',
components: {
default: Top,
Left: Left,
Right: Right
}
}
]
},
}
<template>
<div class="home">
<router-view></router-view>
<router-view name="Left"></router-view>
<router-view name="Right"></router-view>
</div>
</template>
key解决组件复用问题
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。
key解决上述问题之外的情景:这两个元素是完全独立的,不要复用它们。
<div id="root">
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
<button @click="toggleLoginType">Toggle login type</button>
</div>
渲染函数(render)
进一步理解需要查阅资料
<child><p>shantoudaxue</p></child>
Vue.component('child',{
render:function(createElement){
console.log(this.$slots.default)
return createElement('div',this.$slots.default)
}
})
createElement(‘div’,{属性}, createElement(‘d’)) 第三个参数是子节点
不想用computed缓存时,请使用methods方法替代
computed可能出现问题
watch: 多种写法,深度监听
注意: 不应该使用箭头函数来定义 watcher 函数 注意如果你为一个计算属性使用了箭头函数,则 this
不会指向这个组件的实例
watch:{
obj:{ //监听的对象
deep:true, //深度监听设置为 true
handler:function(newV,oldV){
console.log('watch中:',newV)
}
}
}
js引用类型存储 set的使用,解决data中对空对象增加属性无效问题
监听对象:深度监听可以监听对象,要监听对象还可以将对象转成字符串的形式 2种方发监听对象
mixins混入不共享数据,mixin全局混入共享数据???
路由懒加载跟组件懒加载 () => import()
polyfill promise兼容包
computed: mapState({
list: state => state.list
}),
computed: {
…mapState([‘city’])或者 …mapState({ city })
}
渲染函数
mock.js
- 前后端分离
- 不需要修改既有代码,就可以拦截ajax请求,返回模拟的相应数据
- 数据类型丰富
- 通过随机数据,模拟各种场景
this. m e s s a g e . e r r o r ( ) t h i s . message.error() this. message.error()this.message.success() 安装使用elementUI
Vue.js之各类弹框提示
axios跨域: axios.defaults.withCredentials = true
vuex: 模块自动注册 store里面的plugin字段
创建动态store模块
动态创建,就是在需要的地方,再去注册该模块。在页面中,使用如下方式进行手动注册
<script>
import moduleA from '../store/modules/moduleA'
created() {
this.$store.registerModule('moduleA', moduleA);
}
</script>
渲染函数 render的第二个参数
render函数不能使用箭头函数,this指向会报错
Deno
VueSSR : vue服务端渲染
vue-server-render安装
- 创建vue实例
- 创建一个render
- 将vue实例渲染为HTML
相当于
相当于
问题)
监听对象:深度监听可以监听对象,要监听对象还可以将对象转成字符串的形式 2种方发监听对象
mixins混入不共享数据,mixin全局混入共享数据???
路由懒加载跟组件懒加载 () => import()
polyfill promise兼容包
computed: mapState({
list: state => state.list
}),
computed: {
…mapState([‘city’])或者 …mapState({ city })
}
渲染函数
mock.js
- 前后端分离
- 不需要修改既有代码,就可以拦截ajax请求,返回模拟的相应数据
- 数据类型丰富
- 通过随机数据,模拟各种场景
this. m e s s a g e . e r r o r ( ) t h i s . message.error() this. message.error()this.message.success() 安装使用elementUI
Vue.js之各类弹框提示
axios跨域: axios.defaults.withCredentials = true
vuex: 模块自动注册 store里面的plugin字段
创建动态store模块
动态创建,就是在需要的地方,再去注册该模块。在页面中,使用如下方式进行手动注册
<script>
import moduleA from '../store/modules/moduleA'
created() {
this.$store.registerModule('moduleA', moduleA);
}
</script>
渲染函数 render的第二个参数
render函数不能使用箭头函数,this指向会报错
Deno
VueSSR : vue服务端渲染
vue-server-render安装
- 创建vue实例
- 创建一个render
- 将vue实例渲染为HTML
相当于
相当于