vue2
是用过Object.defineProperty
实现数据响应式, 组件初始化时,对 data 中的 item 进行递归遍历,对 item 的每一个属性进行劫持,添加 set , get 方法。我们后来 新加的属性 ,并没有通过Object.defineProperty
设置成响应式数据,修改后不会视图更新
通过数组索引号修改了数组,界面会不会相应更新?为什么?
答:不会。vue 监听不到
vue 为什么没有提供 arr[下标] = val
变成响应式?
尤大:“因为性能问题,性能代价和获得的用户体验收益不成正比”
对象:使用了Object.defineProperty
中的 get 和 set
如何监测对象中的数据?
通过 setter 实现监视,且要在 new Vue 时就传⼊要监测的数据
- 对象中后追加的属性,Vue 默认不做响应式处理
- 如需给后添加的属性做响应式,请使⽤如下 API:
Vue.set(target,propertyName/index,value)
vm.$set(target,propertyName/index,value)
数组: Vue
重写了数组的原型,更准确的表达是拦截了数组的原型
如何监测数组中的数据?
通过包裹数组更新元素的⽅法实现,本质就是做了两件事:
调⽤原⽣对应的⽅法对数组进⾏更新
重新解析模板,进⽽更新⻚⾯
在 Vue 修改数组中的某个元素⼀定要⽤如下⽅法:
- 使 ⽤ 这 些 API :
push() pop() shift() unshift() splice() sort() reverse()
Vue.set()
或vm.$set()
- 覆盖整个数组
为什么对象和数组要分开处理?
对象
的属性通常比较少,对每一个属性都劫持set和get
,并不会消耗很多性能
数组
有可能有成千上万个元素,如果每一个元素都劫持set和get
,无疑消耗太多性能了所以
对象
通过defineProperty
进行正常的劫持set和get
数组
则通过修改数组原型上的部分方法
,来实现修改数组触发响应式
生命周期 | 执行时机 |
---|---|
beforeCreate |
在组件实例被创建之初、组件的属性⽣效之前被调用 |
created |
在组件实例已创建完毕。此时属性也已绑定,但真实 DOM 还未⽣成,$el 还不可⽤ |
beforeMount |
在组件挂载开始之前被调⽤。相关的 render 函数⾸次被调⽤ |
mounted |
在 el 被新建的 vm.$el 替换并挂载到实例上之后被调用 |
beforeUpdate |
在组件数据更新之前调⽤。发⽣在虚拟 DOM 打补丁之前 |
update |
在组件数据更新之后被调用 |
activited |
在组件被激活时调⽤(使用了 的情况下) |
deactivated |
在组件被销毁时调⽤(使用了 的情况下) |
beforeDestory |
在组件销毁前调⽤ |
destoryed |
在组件销毁后调⽤ |
加载渲染过程
->父beforeCreate
->父created
->父beforeMount
->子beforeCreate
->子created
->子beforeMount
->子mounted
->父mounted
更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
keep-alive
可以实现组件缓存,当组件切换时不会对当前组件进行卸载
参考:https://juejin.cn/post/6844903641866829838#heading-11
参考:https://juejin.cn/post/7114252241166401573
官方文档:对于任何复杂逻辑,你都应当使用计算属性
扩展:vue 中 Computed、Methods、Watch 区别
computed(计算属性) | watch(监视属性/侦听器) |
---|---|
根据你所依赖的数据动态显示新的计算结果 不用再 data 中声明,否则报错 |
data 的数据监听回调,依赖 data 的数据变化 直接使用 data 声明的数据 |
支持缓存 | 不支持缓存 |
不支持异步 | 支持异步 |
有 get 和 set 方法,当数据变化时,调用 set 方法 | 可以深度监视 deep,加载就调用 immediate 监听的函数接收两个函数,newVal 和 oldVla |
当需要进行数值计算,并且依赖于其它数据时,用 computed | 在某个数据变化时做一些事情或需要异步操作时,用 watch |
computed 能做的 | watch 都能做到 |
props
$emit
.sync
v-model
$parent / $children
$parent 获取父组件的实例,任意调用父组件的方法,修改父组件的数据ref
父组件获取 子组件 实例,任意调用子组件的方法获取子组件的属性provide / inject
prpvide 父组件内部提供数据 inject 嵌套的子组件可以注入数据$attrs / $listeners
$attrs(没有被 props 接收的所有自定义属性) $listeners(可以获取所有的父组件传递过来的自定义事件)eventBus
定义一个事件总线 使用$on
绑定$emit
触发vuex
- 路由传参
参考:https://www.wpgdadatong.com/cn/blog/detail?BID=B3650
参考:https://juejin.cn/post/7110223595359436813
NextTick 是什么
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
// 修改数据
this.message = "修改后的值";
// 此时DOM还没有更新
console.log(this.$el.textContent); // => '原始的值'
this.$nextTick(function () {
// DOM 更新了
console.log(this.$el.textContent); // => '修改后的值'
});
应用场景:因为 vue 是异步执行 dom 更新的,所以当你希望在更新数据之后,操作新的视图, 那么你的操作逻辑应写在 Vue.nextTick(callback) 的回调中,而这个回调会在dom 更新循环结束之后执行。 否则,因为异步更新 dom 的原因,如果你不是在 Vue.nextTick(callback) 的回调中执行操作新视图, 那么可能会发生意外。例如你在 created()钩子 是不能操作 dom 的,但你可以在此调接口更新数据, 如果你此时希望接口更新完毕数据后,接着调用操作 dom 的逻辑,那么最后将这部分操作 dom 的逻辑, 放置在Vue.nextTick(callback) 的回调函数中。
引用:https://juejin.cn/post/7026867875990208543#heading-22
stop
stop
修饰符的作用是阻止冒泡prevent⭐
prevent
修饰符的作用是阻止默认事件(例如 a 标签的跳转)capture
self
self
修饰符作用是,只有点击事件绑定的本身才会触发事件once
once
修饰符的作用是,事件只执行一次native⭐
native
修饰符是加在自定义组件的事件上,保证事件能执行native
是用来是在父组件中给子组件绑定一个原生的事件,就将子组件变成了普通的 HTML 标签看待passive
sync⭐
当父组件
传值进子组件
,子组件想要改变这个值时,可以这么做
// 父组件里
<children :foo.sync="bar"></children>
// 子组件里
this.$emit('update:foo', newValue)
camel
.camel
修饰符允许在使用 DOM 模板时将 v-bind
property 名称驼峰化,例如 SVG 的 viewBox
property:
<svg :view-box.camel="viewBox"></svg>
left
right
middle
trim⭐
trim
修饰符的作用类似于 JavaScript 中的trim()
方法,作用是把v-model
绑定的值的首尾空格给过滤掉。lazy
lazy
修饰符作用是,改变输入框的值时 value 不会改变,当光标离开输入框时,v-model
绑定的值 value 才会改变number
number
修饰符的作用是将值转成数字,但是先输入字符串和先输入数字,是两种情况:先输入数字的话,只取前面数字部分
先输入字母的话,
number
修饰符无效
.ctrl
、.alt
、.shift
、.meta
配合键盘事件使用:
.enter
.tab
.delete
.esc
.space
.up
.down
left
…
FileReader 与 URL.createObjectURL 实现图片、视频上传预览
event.target.files 就是用户上传的图片信息
配合 cropperjs 可以实现图片裁剪功能
// 如果接口要求 Content-Type 是 multipart/form-data
// 则你必须传递 FormData 对象
为什么 v-if 和 v-for 不能同时使用?
v-if 不能和 v-for 一起使用的原因是 v-for 的优先级比 v-if 高,一起使用会造成性能浪费
解决方案有两种,把 v-if 放在 v-for 的外层或者把需要 v-for 的属性先从计算属性中过滤一次
v-if 和 v-for 的优先级问题在 vue3 中不需要考虑,vue3 更新了 v-if 和 v-for 的优先级,使 v-if 的优先级高于 v-for
v-if 和 v-show
v-show
隐藏则是为该元素添加display:none
。v-if
是将dom
元素整个添加或删除
v-show
由false
变为true
的时候不会触发组件的生命周期v-if
由false
变为true
的时候,触发组件的beforeCreate
、create
、beforeMount
、mounted
钩子,由true
变为false
的时候触发组件的beforeDestory
、destoryed
方法性能消耗:v-if
有更高的切换消耗;v-show
有更高的初始渲染消耗;
如果需要非常频繁地切换,则使用 v-show 较好
如果在运行时条件很少改变,则使用 v-if 较好
Vue.use
是用来安装插件的
用法:Vue.use(plugin)
install
方法。会将 Vue 作为参数传入
。总结:Vue.use 是官方提供给开发者的一个 api,用来注册、安装类似 Vuex、vue-router、ElementUI 之类的插件的
线上环境的跨域问题
1、cors >> 后端加响应头 access-control-allow-origin: ‘*’ 允许所有的页面请求
2、代理(主流方案)
开发环境 :webpack的devServer进行代理,本地请求转发到接口 vue.config.js > devServer > proxy {}
线上环境: 服务器端使用 nginx 反向代理,拦截请求转发到线上接口, 解决跨域问题
跨域问题是浏览器的同源策略所导致的
其中,域名、协议、端口号相同,称之为同源,如果不同,称之为跨源或跨域
跨域常见的解决方法:
代理适用的场景是:生产环境不发生跨域,但开发环境发生跨域
因此,只需要在开发环境使用代理解决跨域即可,这种代理又称之为开发代理
在实际开发中,只需要对开发服务器稍加配置即可完成
// vue 的开发服务器代理配置
// vue.config.js
module.exports = {
devServer: {
// 配置开发服务器
proxy: {
// 配置代理
"/api": {
// 若请求路径以 /api 开头
target: "http://dev.taobao.com", // 将其转发到 http://dev.taobao.com
},
},
},
};
参考:阮一峰 CORS: https://www.ruanyifeng.com/blog/2016/04/cors.html
CORS
是基于http1.1
的一种跨域解决方案,它的全称是Cross-Origin Resource Sharing,跨域资源共享。CORS 需要浏览器和后端同时支持。
整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。
浏览器将 CORS 请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)
只要同时满足以下两大条件,就属于简单请求。
凡是不同时满足下面两个条件,就属于非简单请求。
(1) 请求方法是以下三种方法之一:
- HEAD
- GET
- POST
(2)HTTP 的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
JSONP 的做法是:当需要跨域请求时,不使用 AJAX,转而生成一个 script 元素去请求服务器,由于浏览器并不阻止 script 元素的请求,这样请求可以到达服务器。服务器拿到请求后,响应一段 JS 代码,这段代码实际上是一个函数调用,调用的是客户端预先生成好的函数,并把浏览器需要的数据作为参数传递到函数中,从而间接的把数据传递给客户端
JSONP 优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持 get 方法具有局限性,不安全可能会遭受 XSS 攻击。
什么是 cookie
cookie 是储存在用户本地终端上的数据,是网站为了识别用户和跟踪会话而存储在用户本地终端中的文本数据
怎么操作
可以使用
js-cookie
插件模块化开发时直接引入
import Cookies from 'js-cookie'
js-cookie.js 常用的 API 和方法
设置 cookie
Cookies.set("name", "value", { expires: 7, path: "" }); //7天过期 Cookies.set("name", { foo: "bar" }); //设置一个json
读取 cookie
Cookies.get("name"); //获取cookie Cookies.get(); //读取所有的cookie
删除 cookie
Cookies.remove("name"); //删除cookie时必须是同一个路径。
- vue 自带的组件 >> 主要功能是缓存组件 >> 提升性能
- 使用场景:可以少网络请求,如果当前组件数据量比较大,就可以节省网络请求 >> 提升用户体验
- 举例:如果详情页面之间进行切换,就可以使用
keep-alive
进行缓存组件,防止同样的数据重复请求
keep-alive
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们
keep-alive
可以设置以下props
属性:
include
- 字符串或正则表达式。只有名称匹配的组件会被缓存exclude
- 字符串或正则表达式。任何名称匹配的组件都不会被缓存max
- 数字。最多可以缓存多少组件实例关于keep-alive
的基本用法:
<keep-alive>
<component :is="view"></component>
</keep-alive>
匹配首先检查组件自身的 name
选项,如果 name
选项不可用,则匹配它的局部注册名称 (父组件 components
选项的键值),匿名组件不能被匹配
设置了 keep-alive 缓存的组件,会多出两个生命周期钩子(activated
与deactivated
):
beforeRouteEnter
> beforeCreate
> created
> mounted
> activated
> … … > beforeRouteLeave
> deactivated
beforeRouteEnter
>activated
> … … > beforeRouteLeave
> deactivated
使用原则:当我们在某些场景下不需要让页面重新加载时我们可以使用keep-alive
举个栗子:
当我们从首页
–>列表页
–>商详页
–>再返回
,这时候列表页应该是需要keep-alive
从首页
–>列表页
–>商详页
–>返回到列表页(需要缓存)
–>返回到首页(需要缓存)
–>再次进入列表页(不需要缓存)
,这时候可以按需来控制页面的keep-alive
在路由中设置keepAlive
属性判断是否需要缓存
{
path: 'list',
name: 'itemList', // 列表页
component (resolve) {
require(['@/pages/item/list'], resolve)
},
meta: {
keepAlive: true,
title: '列表页'
}
}
使用
<div id="app" class='wrapper'>
<keep-alive>
<!-- 需要缓存的视图组件 -->
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<!-- 不需要缓存的视图组件 -->
<router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
解决方案可以有以下两种:
作用:
作用:使样式私有化(模块化),不对全局造成污染
原理:动态的给组件加上一个 hash 值,用属性选择器去匹配
$route:当前的路由信息对象,获取到路由参数、路径
- $route.path: 字符串,对应当前路由的路径,总是解析为绝对路径,如
/foo/bar
。- $route.params: 一个 key/value 对象,包含了 动态片段 和 全匹配片段,如果没有路由参数,就是一个空对象。
- r o u t e . q u e r y :一个 k e y / v a l u e 对象,表示 U R L 查询参数。例如,对于路径 / f o o ? u s e r = 1 ,则有 route.query: 一个 key/value 对象,表示 URL 查询参数。例如,对于路径 /foo?user=1,则有 route.query:一个key/value对象,表示URL查询参数。例如,对于路径/foo?user=1,则有route.query.user == 1,如果没有查询参数,则是个空对象。
- $route.hash: 当前路由的 hash 值 (不带#) ,如果没有 hash 值,则为空字符串。锚点
- $route.fullPath: 完成解析后的 URL,包含查询参数和 hash 的完整路径。
- $route.matched: 数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。
- $route.name: 当前路径名字
- $route.meta: 路由元信息
r o u t e r :全局路由 v u e R o u t e r 的实例,挂载到 V u e 原型上 router:全局路由 vueRouter 的实例,挂载到 Vue 原型上 router:全局路由vueRouter的实例,挂载到Vue原型上router 属性,可以获取到全局路由配置信息,跳转方法
$router.replace({path:‘home’}),//替换路由,没有历史记录
$router.push(‘/login’) ,跳转到指定路由
$router.back()
$router.go()
发布订阅模式中有三个角色,发布者
Publisher
,信息中心Event Channel
,订阅者Subscriber
我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern)
通常是通过 on 事件订阅消息,emit 事件发布消息,remove 事件删除订阅
class PubSub {
constructor() {
// 事件中心
// 存储格式: warTask: [], routeTask: []
// 每种事件(任务)下存放其订阅者的回调函数
this.events = {};
}
// 订阅方法
subscribe(type, cb) {
if (!this.events[type]) {
}
this.events[type].push(cb);
}
// 发布方法
publish(type, ...args) {
if (this.events[type]) {
this.events[type].forEach((cb) => cb(...args));
}
}
// 取消订阅方法
unsubscribe(type, cb) {
if (this.events[type]) {
const cbIndex = this.events[type].findIndex((e) => e === cb);
if (cbIndex !== -1) {
this.events[type].splice(cbIndex, 1);
}
}
if (this.events[type].length === 0) {
delete this.events[type];
}
}
unsubscribeAll(type) {
if (this.events[type]) {
delete this.events[type];
}
}
}
当对象之间存在一对多的依赖关系时,其中一个对象的状态发生改变,所有依赖它的对象都会收到通知,这就是观察者模式
- 观察者(订阅者) – Watcher
- update():当事件发生时,具体要做的事情
- 目标(发布者) – Dep
- subs 数组:存储所有的观察者
- addSub():添加观察者
- notify():当事件发生,调用所有观察者的 update() 方法
- 没有事件中心
设计模式 | 观察者模式 | 发布订阅模式 |
---|---|---|
主体 | Watcher 观察者、Dep 目标对象 | Publisher 发布者、Event Channel 信息中心、Subscribe 订阅者 |
主体关系 |
Dep 中通过 subs 记录 Watcher | Publisher 和 Subscribe 不想不知道对方,通过中介联系 |
优点 | 角色明确,Watcher 和 Dep 要遵循约定的成员方法 | 松散耦合,灵活度高,通常应用在异步编程中 |
缺点 | 紧耦合 | 当事件类型变多时,会增加维护成本 |
使用案例 | 双向数据绑定 | 事件总线 EventBus |
相同点:都是语法糖,都可以实现父子组件中的数据的双向通信。
// v-model <son v-model="num"/> //父组件使用子组件 model:{ prop:'newValue', // 默认为 value 可以使用prop自定义属性名 event:"updateValue", // event 修改事件名 默认为input }, props: { // 子组件接收 value: { // 默认为value type: Number, } } this.$emit('input', '小红') // .sync <son :title.sync="doc.title"></son> // 父组件 props:{ title:{ type:... } } this.$emit('update:title', newTitle) // 子组件
区别点:格式不同: v-model=“num”, :num.sync=“num”
v-model
: @input + value
:num.sync
: @update:num另外需要特别注意的是:
v-model
只能用一次;.sync
可以有多个
背景:修改当前组件嵌套的子组件内部的样式
问题:
如何解决:添加/deep/
/ ::v-deep
scss: 使用
::v-deep
less: 使用
/deep/
SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。
优点:
缺点:
① 单向数据流是指数据从父组件传向子组件,子组件没有权限直接修改该数据;
② 子组件需要在 data 或者 computed 中重新定义变量来接收父组件传来的值,以便修改;
③ 子组件可以通过 $emit 的方式通知父组件修改值,再重新传回给子组件;
为给客户更好的客户体验,首屏组件加载速度更快一些,解决白屏问题
懒加载简单来说就是延迟加载或按需加载,即在需要的时候的时候进行加载
路由 和 组件 的常用两种懒加载方式:
1、vue异步组件实现路由懒加载
component:resolve=>(['需要加载的路由的地址',resolve])
2、es提出的import(推荐使用这种方式)
const HelloWorld = ()=> import('需要加载的模块地址')
通过魔法注释webpackChunkName修改名称,打包时就会显示修改后的新名称
const { name } = await import(/* webpackChunkName:"newName"*/'路径')
key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速,以便高效的更新虚拟 DOM** 1. 对⽐规则: 1. 旧虚拟DOM中找到了与新虚拟DOM相同的key: 1. 若虚拟DOM中内容没变, 直接使⽤之前的真实DOM 2. 若虚拟DOM中内容变了, 则⽣成新的真实DOM,随后替换掉⻚⾯中之前的真 实DOM 2. 旧虚拟DOM中未找到与新虚拟DOM相同的key:创建新的真实DOM,随后渲染到 到⻚⾯ 2. ⽤index作为key可能会引发的问题: 1. 若对数据进⾏逆序添加、逆序删除等破坏顺序操作:会产⽣没有必要的真实DOM 更新 ==> 界⾯效果没问题, 但效率低 2. 若结构中还包含输⼊类的DOM:会产⽣错误DOM更新 ==> 界⾯有问题 3. 开发中如何选择key? 1. 最好使⽤每条数据的唯⼀标识作为key,⽐如id、⼿机号、⾝份证号、学号等唯⼀ 值 2. 如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅⽤于渲染列表, 使⽤index作为key是没有问题的
参考:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html
参考:https://router.vuejs.org/zh/guide/advanced/meta.html
全局导航钩子
router.beforeEach(to,from,next)
:添加一个导航守卫,在任何导航前执行router.beforeResolve(to,from)
:添加一个导航守卫,在导航即将解析之前执行。在这个状态下,所有的组件都已经被获取,并且其他导航守卫也已经成功router.afterEach(to,from)
:添加一个导航钩子,在每次导航后执行组件内的钩子
beforeRouterEnter(to, from, next)
:在渲染该组件的对应路由被验证前调用beforeRouterUpdate
:在当前路由改变,但是该组件被复用时调用beforeRouterLeave
:在导航离开渲染该组件的对应路由时调用const UserDetails = {
template: `...`,
beforeRouteEnter(to, from,next) {
// 在渲染该组件的对应路由被验证前调用
// 不能获取组件实例 `this` !
// 因为当守卫执行时,组件实例还没被创建!
},
beforeRouteUpdate(to, from) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
// 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
},
beforeRouteLeave(to, from) {
// 在导航离开渲染该组件的对应路由时调用
// 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
},
}
路由独享的守卫
beforeEnter
:只在进入路由时触发,不会在 params
、query
或 hash
改变时触发const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: (to, from) => {
// reject the navigation
return false
},
},
]
- Model:模型层(数据层),主要用于保存一些数据
- View: 视图层,主要用于将后端数据借助各种元素呈现给用户,同时也可提供用户操作的入口
- ViewModel:视图模型层:该层也是mvvm中的核心层,主要用于作为Model个View两个层的数据连接层,负责两个层之间的数据传递。该层主要包含两大功能点:
- DOM监听(DOM Listener)视图变化后更新数据
- 数据绑定(Data bindings)数据变化后更新视图
参考:https://juejin.cn/post/6950939562872930341
Vue 数据双向绑定主要是指:数据变化更新视图,视图变化更新数据
Vue 主要通过以下 4 个步骤来实现数据双向绑定的:
以上四个步骤的流程图表示如下,如果有同学理解不大清晰的,可以查看作者专门介绍数据双向绑定的文章《0 到 1 掌握:Vue 核心之数据双向绑定》,有进行详细的讲解、以及代码 demo 示例
参考:http://t.csdn.cn/f9CHg
我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
以 input 表单元素为例:
<input v-model='something'>
// 相当于
<input v-bind:value="something" v-on:input="something = $event.target.value">
首先,data是个函数是确保数据不会被污染,在JS中对象是一个引用数据类型,如果是对象形式,多个vue实例会相互影响,为了保证组件的复用性,data要写成函数,函数形式每次使用时,会返回一个新的对象拷贝,这样使用组件时data中的数据就不会被相互影响
new Vue的实例,比如app.js中,是不会被复用的,所以不存在数据污染的情况
hash模式
history模式
abstract模式
参考:https://juejin.cn/post/6867875626611654663
前端路由的核心,就在于改变视图的同时不会向后端发出请求;而是加载路由对应的组件。vue-router就是将组件映射到路由, 然后渲染出来的。并实现了三种模式:Hash模式、History模式以及Abstract模式。默认Hash模式
hash模式 : #后面是路由路径,特点是前端访问,#后面的变化不会经过服务器
history模式:正常的/访问模式,特点是后端访问,任意地址的变化都会访问服务器
工作时没用过,支持所有javascript运行模式。vue-router 自身会对环境做校验,如果发现没有浏览器的 API,路由会自动强制进入 abstract 模式。在移动端原生环境中也是使用 abstract 模式。
hash 模式和 history 模式都属于浏览器自身的特性,Vue-Router 只是利用了这两个特性(通过调用浏览器提供的接口)来实现前端路由。
参考:https://vue3js.cn/interview/vue/slot.html#%E4%B8%80%E3%80%81slot%E6%98%AF%E4%BB%80%E4%B9%88
slot,是组件的一块HTML模板,这块模板显示不显示、以及怎样显示由父组件来决定
通过插槽可以让用户可以拓展组件,去更好地复用组件和对其做定制化处理
如果父组件在使用到一个复用组件的时候,获取这个组件在不同的地方有少量的更改,如果去重写组件是一件不明智的事情
通过
slot
插槽向组件内部指定位置传递内容,完成这个复用组件在不同场景的应用比如布局组件、表格列、下拉选、弹框显示内容等
默认插槽
子组件用
标签来确定渲染的位置,标签里面可以放
DOM
结构,当父组件使用的时候没有往插槽传入内容,标签内DOM
结构就会显示在页面父组件在使用的时候,直接在子组件的标签内写入内容即可
具名插槽
子组件用
name
属性来表示插槽的名字,不传为默认插槽父组件中在使用时在默认插槽的基础上加上
slot
属性,值为子组件插槽name
属性值作用域插槽
子组件在作用域上绑定属性来将子组件的信息传给父组件使用,这些属性会被挂在父组件
v-slot
接受的对象上父组件中在使用时通过
v-slot:
(简写:#)获取子组件的信息,在内容中使用
服务器端渲染