vue的面试题
问题一:构建的 vue-cli 工程都到了哪些技术,它们的作用分别是什么?1、vue.js:vue-cli工程的核心,主要特点是 双向数据绑定 和 组件系统。2、vue-router:vue官方推荐使用的路由框架。3、vuex:专为 Vue.js 应用项目开发的状态管理器,主要用于维护vue组件间共用的一些 变量 和 方法。4、axios( 或者 fetch 、ajax ):用于发起 G...
问题一:构建的 vue-cli 工程都到了哪些技术,它们的作用分别是什么?
1、vue.js:vue-cli工程的核心,主要特点是 双向数据绑定 和 组件系统。
2、vue-router:vue官方推荐使用的路由框架。
3、vuex:专为 Vue.js 应用项目开发的状态管理器,主要用于维护vue组件间共用的一些 变量 和 方法。
4、axios( 或者 fetch 、ajax ):用于发起 GET 、或 POST 等 http请求,基于 Promise 设计。
5、vux等:一个专为vue设计的移动端UI组件库。
6、创建一个emit.js文件,用于vue事件机制的管理。
7、webpack:模块加载和vue-cli工程打包器。
Vue-cli 工程常用的 npm 命令有哪些?
下载 node_modules 资源包的命令:
npm install
启动 vue-cli 开发环境的 npm命令:
npm run dev
vue-cli 生成 生产环境部署资源 的 npm命令
npm run build
用于查看 vue-cli 生产环境部署资源文件大小的 npm命令:
npm run build --report
请说出vue-cli工程中每个文件夹和文件的用处
vue-cli目录解析:
1.build 文件夹:用于存放 webpack 相关配置和脚本。开发中仅 偶尔使用 到此文件夹下 webpack.base.conf.js
用于配置 less、sass等css预编译库,或者配置一下 UI 库。 config
2.文件夹:主要存放配置文件,用于区分开发环境、线上环境的不同。 常用到此文件夹下 config.js 配置开发环境的
3. 端口号、是否开启热加载 或者 设置生产环境的静态资源相对路径、是否开启gzip压缩、npm run build
4.命令打包生成静态资源的名称和路径等。 dist 文件夹:默认 npm run build 命令打包生成的静态资源文件,用于生产部署。
5.node_modules:存放npm命令下载的开发环境和生产环境的依赖包。 src: 存放项目源码及需要引用的资源文件。
6. src下assets:存放项目中需要用到的资源文件,css、js、images等。
7.src下componets:存放vue开发中一些公共组件:header.vue、footer.vue等。
8.src下emit:自己配置的vue集中式事件管理机制。 src下router:vue-router vue路由的配置文件。
9.src下service:自己配置的vue请求后台接口方法。 src下page:存在vue页面组件的文件夹。
10. src下util:存放vue开发过程中一些公共的.js方法。 src下vuex:存放 vuex 为vue专门开发的状态管理器。
11.src下app.vue:使用标签<route-view></router-view>渲染整个工程的.vue组件。
12.src下main.js:vue-cli工程的入口文件。 index.html:设置项目的一些meta头信息和提供<div id="app"></div>用于挂载 vue 节点。 package.json:用于 node_modules资源部 和启动、打包项目的 npm 命令管理.
config文件夹 下 index.js 的对于工程 开发环境 和 生产环境 的配置
build 对象下 对于 生产环境 的配置:
index:配置打包后入口.html文件的名称以及文件夹名称
assetsRoot:配置打包后生成的文件名称和路径
assetsPublicPath:配置 打包后 .html 引用静态资源的路径,一般要设置成 "./"
productionGzip:是否开发 gzip 压缩,以提升加载速度
dev 对象下 对于 开发环境 的配置:
port:设置端口号
autoOpenBrowser:启动工程时,自动打开浏览器
proxyTable:vue设置的代理,用以解决 跨域 问题
`
请你详细介绍一些 package.json 里面的配置
常用对象解析:
scripts:npm run xxx 命令调用node执行的 .js 文件
dependencies:生产环境依赖包的名称和版本号,即这些 依赖包 都会打包进 生产环境的JS文件里面
devDependencies:开发环境依赖包的名称和版本号,即这些 依赖包 只用于 代码开发 的时候,不会打包进 生产环境js文件 里面
对于Vue是一套渐进式框架的理解
vue.js的两个核心是什么?
1.数据驱动,也叫双向绑定
vue的数据观测原理技术是实现上,利用的是ES5Object.defineProperty和存储器属性: getter和setter(IE9以上版本兼容),称为基于依赖收集的观测机制。核心是vm,保证数据和视图的一致性。
2.组件系统
vue组件的核心选项:
1、模板(template):模板声明了数据和最终展现给用户的DOM之间的映射关系。
2、初始数据(data):一个组件的初始数据状态。对于可复用的组件来说,这通常是私有的状态。
3、接受的外部参数(props):组件之间通过参数来进行数据的传递和共享。
4、方法(methods):对数据的改动操作一般都在组件的方法内进行。
5、生命周期钩子函数(lifecycle hooks):一个组件会触发多个生命周期钩子函数,最新2.0版本对于生命周期函数名称改动很大。
6、私有资源(assets):Vue.js当中将用户自定义的指令、过滤器、组件等统称为资源。一个组件可以声明自己的私有资源。私有资源只有该组件和它的子组件可以调用。
对于 Vue 是一套 构建用户界面 的 渐进式框架 的理解
渐进式代表的含义是:没有多做职责之外的事。
vue.js只提供了 vue-cli 生态中最核心的 组件系统 和 双向数据绑定。
像vuex、vue-router都属于围绕 vue.js开发的库。
比如说,你要使用Angular,必须接受以下东西:
必须使用它的模块机制
必须使用它的依赖注入-
必须使用它的特殊形式定义组件(这一点每个视图框架都有,难以避免)
所以Angular是带有比较强的排它性的,如果你的应用不是从头开始,而是要不断考虑是否跟其他东西集成,这些主张会带来一些困扰。
比如说,你要使用React,你必须理解:
函数式编程的理念,
需要知道什么是副作用,
什么是纯函数,
如何隔离副作用
它的侵入性看似没有Angular那么强,主要因为它是软性侵入。
Vue与React、Angular的不同是,但它是渐进的:
你可以在原有大系统的上面,把一两个组件改用它实现,当jQuery用;
也可以整个用它全家桶开发,当Angular用;
还可以用它的视图,搭配你自己设计的整个下层用。
你可以在底层数据逻辑的地方用OO和设计模式的那套理念,
也可以函数式,都可以,它只是个轻量视图而已,只做了最核心的东西。
请说出vue几种常用的指令
- v-if:根据表达式的值的真假条件渲染元素。在切换时元素及它的数据绑定/组件被销毁并重建。
- v-show:根据表达式之真假值,切换元素的 display CSS 属性。
- v-for:循环指令,基于一个数组或者对象渲染一个列表,vue 2.0以上必须需配合 key值 使用。
- v-bind:动态地绑定一个或多个特性,或一个组件 prop 到表达式。
- v-on:用于监听指定元素的DOM事件
- v-model:用于实现表单输入和应用状态间的双向绑定
- v-pre:跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量的没有指令的节点加快编译
- v-once:只渲染元素和组件一次。随后重新渲染,元素组件及其所有的子节点将被视为静态的内容并跳过。可以用于性能的优化。
请问 v-if 和 v-show 有什么区别
共同点:
v-if 和 v-show 都是动态显示DOM元素。
区别:
1、编译过程: v-if 是 真正 的 条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性display。
2、编译条件: v-if 是惰性的:如果在初始渲染时条件为假,则什么也不做。直到条件第一次变为真时,才会开始渲染条件块。v-show不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
3、性能消耗: v-if有更高的切换消耗。v-show有更高的初始渲染消耗。
4、应用场景: v-if适合运行时条件很少改变时使用。v-show适合频繁切换。
vue常用的修饰符
v-on 指令常用修饰符:
- .stop - 调用 event.stopPropagation(),禁止事件冒泡。
- .prevent -调用event.preventDefault(),阻止事件的默认行为
- .capture - 添加事件侦听器时使用capture模式
- .self -只当事件是从侦听器绑定的元素本身触发时才触发回调。
- .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
- .native - 监听组件根元素的原生事件。
- .once - 只触发一次回调。
- .left - (2.2.0) 只当点击鼠标左键时触发。
- .right - (2.2.0) 只当点击鼠标右键时触发。
- .middle - (2.2.0) 只当点击鼠标中键时触发。
- .passive - (2.3.0) 以 { passive: true } 模式添加侦听器
注意: 如果是在自己封装的组件或者是使用一些第三方的UI库时,会发现并不起效果,这时就需要用`·.native修饰符了,如:
//使用示例:
<el-input
v-model="inputName"
placeholder="搜索你的文件"
@keyup.enter.native="searchFile(params)"
>
</el-input>
v-on可以监听多个方法吗?
v-on可以监听多个方法,如:
<input type=“text” :value=“name” @input=“onInput” @focus=“onFocus” @blur=“onBlur” />
但是同一种事件类型的方法,vue-cli工程会报错
<a href=“javascript:;” @click=“methodsOne” @click=“methodsTwo”>
v-bind 指令常用修饰符:
- .prop - 被用于绑定 DOM 属性 (property)。(差别在哪里?)、
- .camel - (2.1.0+) 将 kebab-case 特性名转换为 camelCase. (从 2.1.0 开始支持)
- .sync (2.3.0+) 语法糖,会扩展成一个更新父组件绑定值的 v-on 侦听器。
v-model 指令常用修饰符:
- .lazy-取代input监听change事件
- .number-输入字符串转为数字
- .trim-输入收尾空格过滤
vue中 key 值的作用
key值:用于 管理可复用的元素。因为Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做使 Vue 变得非常快,但是这样也不总是符合实际需求。
2.2.0+ 的版本里,当在组件中使用 v-for 时,key 现在是必须的
vue-cli如何进行工程升级?
前言:此命令谨慎使用,实际开发中如需升级建议直接使用 vue-cli 脚手架搭建,只需要了解即可!
推荐使用
升级插件
npm-check-updates
首先安装插件,建议使用npm源安装,测试cnpm安装下载成功
npm install npm-check-updates -g
然后在待升级工程的目录结构下,命令行输入:
ncu
执行效果
然后升级所有版本,命令行输入:
ncu -a
再输入
ncu install
vue事件中如何使用event对象?
注意在事件中要使用 $ 符号
//html部分
<a href="javascript:void(0);" data-id="12" @click="showEvent($event)">event</a>
//js部分
showEvent(event){
//获取自定义data-id
console.log(event.target.dataset.id)
//阻止事件冒泡
event.stopPropagation();
//阻止默认
event.preventDefault()
}
```什么是$nextTick?````
简单回答
因为Vue的异步更新队列,$nextTick是用来知道什么时候DOM更新完成的。
详细解读:
我们先来看这样一个场景:有一个div,默认用 v-if 将它隐藏,点击一个按钮后,改变 v-if 的值,让它显示出来,同时拿到这个div的文本内容。如果v-if的值是 false,直接去获取div内容是获取不到的,因为此时div还没有被创建出来,那么应该在点击按钮后,改变v-if的值为 true,div才会被创建,此时再去获取,示例代码如下:
<div id="app">
<div id="div" v-if="showDiv">这是一段文本</div>
<button @click="getText">获取div内容</button>
</div>
<script>
var app = new Vue({
el : "#app",
data:{
showDiv : false
},
methods:{
getText:function(){
this.showDiv = true;
var text = document.getElementById('div').innnerHTML;
console.log(text);
}
}
})
</script>
这段代码并不难理解,但是运行后在控制台会抛出一个错误:Cannot read property 'innnerHTML of null,意思就是获取不到div元素。这里就涉及Vue一个重要的概念:异步更新队列。
异步更新队列
Vue在观察到数据变化时并不是直接更新DOM,而是开启一个队列,并缓冲在同一个事件循环中发生的所以数据改变。在缓冲时会去除重复数据,从而避免不必要的计算和DOM操作。然后,在下一个事件循环tick中,Vue刷新队列并执行实际(已去重的)工作。所以如果你用一个for循环来动态改变数据100次,其实它只会应用最后一次改变,如果没有这种机制,DOM就要重绘100次,这固然是一个很大的开销。
Vue会根据当前浏览器环境优先使用原生的Promise.then和MutationObserver,如果都不支持,就会采用setTimeout代替。
知道了Vue异步更新DOM的原理,上面示例的报错也就不难理解了。事实上,在执行this.showDiv = true时,div仍然还是没有被创建出来,直到下一个vue事件循环时,才开始创建。$nextTick就是用来知道什么时候DOM更新完成的,所以上面的示例代码需要修改为:
<div id="app">
<div id="div" v-if="showDiv">这是一段文本</div>
<button @click="getText">获取div内容</button>
</div>
<script>
var app = new Vue({
el : "#app",
data:{
showDiv : false
},
methods:{
getText:function(){
this.showDiv = true;
this.$nextTick(function(){
var text = document.getElementById('div').innnerHTML;
console.log(text);
});
}
}
})
</script>
这时再点击事件,控制台就打印出div的内容“这是一段文本“了。
理论上,我们应该不用去主动操作DOM,因为Vue的核心思想就是数据驱动DOM,但在很多业务里,我们避免不了会使用一些第三方库,比如 popper.js、swiper等,这些基于原生javascript的库都有创建和更新及销毁的完整生命周期,与Vue配合使用时,就要利用好$nextTick。
Vue 组件中 data 为什么必须是函数?
简单回答
//为什么data函数里面要return一个对象
<script>
export default {
data() {
return { // 返回一个唯一的对象,不要和其他组件共用一个对象进行返回
menu: MENU.data,
poi: POILIST.data
}
}
}
</script>
因为一个组件是可以共享的,但他们的data是私有的,所以每个组件都要return一个新的data对象,返回一个唯一的对象,不要和其他组件共用一个对象。
详细的解答
Vue.component('my-component', {
template: '<div>OK</div>',
data() {
return {} // 返回一个唯一的对象,不要和其他组件共用一个对象进行返回
},
})
这个操作是一个简易操作,实际上
- 首先需要创建一个组件构造器,
- 然后注册组件。
- 注册组件的本质其实就是建立一个组件构造器的引用。
- 使用组件才是真正创建一个组件实例。
- 所以,注册组件其实并不产生新的组件类,但会产生一个可以用来实例化的新方式。
理解这点之后,再理解js的原型链:
var MyComponent = function() {}
MyComponent.prototype.data = {
a: 1,
b: 2,
}
上面是一个虚拟的组件构造器,真实的组件构造器方法很多
var component1 = new MyComponent()
var component2 = new MyComponent()
上面实例化出来两个组件实例,也就是通过调用,创建的两个实例
component1.data.a === component2.data.a // true
component1.data.b = 5
component2.data.b // 5
可以看到上面代码中最后三句,这就比较坑爹了,如果两个实例同时引用一个对象,那么当你修改其中一个属性的时候,另外一个实例也会跟着改。这怎么可以,两个实例应该有自己各自的域才对。所以,需要通过下面方法来进行处理:
var MyComponent = function() {
this.data = this.data()
}
MyComponent.prototype.data = function() {
return {
a: 1,
b: 2,
}
}
这样每一个实例的data属性都是独立的,不会相互影响了。所以,你现在知道为什么vue组件的data必须是函数了吧。这都是因为js本身的特性带来的,跟vue本身设计无关。
v-for 与 v-if 的优先级
当它们处于同一节点,v-for的优先级比v-if更高,这意味着 v-if将分别重复运行于每个 v-for循环中。当你想为仅有的一些项渲染节点时,这种优先级的机制会十分有用,如下:
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
vue中子组件调用父组件的方法
通过v-on 监听 和$emit触发来实现:
- 在父组件中 通过v-on 监听 当前实例上的 自定义事件。
- 在子组件 中 通过’$emit’触发 当前实例上的 自定义事件。
示例:
父组件:
<template>
<div class="fatherPageWrap">
<h1>这是父组件</h1>
<!-- 引入子组件,v-on监听自定义事件 -->
<emitChild v-on:emitMethods="fatherMethod"></emitChild>
</div>
</template>
<script type="text/javascript">
import emitChild from '@/page/children/emitChild.vue';
export default{
data () {
return {}
},
components : {
emitChild
},
methods : {
fatherMethod(params){
alert(JSON.stringify(params));
}
}
}
</script>
子组件:
<template>
<div class="childPageWrap">
<h1>这是子组件</h1>
</div>
</template>
<script type="text/javascript">
export default{
data () {
return {}
},
mounted () {
//通过 emit 触发
this.$emit('emitMethods',{"name" : 123});
}
}
</script>
结果:
子组件 会调用 父组件的fatherMethod
方法,该并且会alert
传递过去的参数:{"name":123}
vue中 keep-alive 组件的作用
keep-alive
:主要用于保留组件状态
或避免重新渲染
。
比如: 有一个列表页面
和一个详情页面
,那么用户就会经常执行打开详情=>返回列表=>打开详情
这样的话 列表 和 详情 都是一个频率很高
的页面,那么就可以对列表组件
使用<keep-alive></keep-alive>
进行缓存,这样用户每次返回列表
的时候,都能从缓存中快速渲染
,而不是重新渲染
。
1、属性:
include
:字符串或正则表达式。只有匹配的组件会被缓存
-exclude
:字符串或正则表达式。任何匹配的组件都不会被缓存。
2、用法:
包裹动态组件
时,会缓存
不活动的组件实例,而不是销毁
它们。和 <transition>
相似,<keep-alive>
是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。
当组件在 内被切换,在 2.2.0 及其更高版本中,activated 和 deactivated生命周期 将会在 树内的所有嵌套组件中触发。
<!-- 基本 -->
<keep-alive>
<component :is="view"></component>
</keep-alive>
<!-- 多个条件判断的子组件 -->
<keep-alive>
<comp-a v-if="a > 1"></comp-a>
<comp-b v-else></comp-b>
</keep-alive>
<!-- 和 `<transition>` 一起使用 -->
<transition>
<keep-alive>
<component :is="view"></component>
</keep-alive>
</transition>
注意:
<keep-alive>
是用在其一个直属的子组件被开关的情形。如果你在其中有 v-for 则不会工作。如果有上述的多个条件性的子元素,<keep-alive>
要求同时只有一个子元素被渲染。
3、include 和 exclude 属性的使用:
2.1.0 新增
include
和 exclude
属性允许组件有条件地缓存。二者都可以用逗号
分隔字符串、正则表达式
或一个数组
来表示:
<!-- 逗号分隔字符串 -->
<keep-alive include="a,b">
<component :is="view"></component>
</keep-alive>
<!-- 正则表达式 (使用 `v-bind`) -->
<keep-alive :include="/a|b/">
<component :is="view"></component>
</keep-alive>
<!-- 数组 (使用 `v-bind`) -->
<keep-alive :include="['a', 'b']">
<component :is="view"></component>
</keep-alive>
匹配首先检查组件自身的 name 选项
,如果 name 选项不可用,则匹配它的局部注册名称
(父组件 components 选项的键值)。匿名组件不能被匹配。
不会在函数式组件中正常工作,因为它们没有缓存实例。
vue中如何编写可复用的组件?
在编写组件的时候,时刻考虑组件是否可复用是有好处的。一次性组件跟其他组件紧密耦合没关系,但是可复用组件一定要定义一个清晰的公开接口。
Vue.js组件 API 来自 三部分:prop
、事件
、slot
:
- prop 允许外部环境传递数据给组件,在vue-cli工程中也可以使用vuex等传递数据。
- 事件允许组件触发外部环境的 action
- slot 允许外部环境将内容插入到组件的视图结构内。
代码示例:
<my-component
:foo="bar"
:bar="qux"
//子组件调用父组件方法
@event-a="doThis"
@event-b="doThat">
<!-- content -->
<img slot="icon" src="..." />
<p slot="main-text">Hello!</p>
</my-component>
什么是vue生命周期和生命周期钩子函数?
1、vue生命周期和生命周期钩子函数:
vue的生命周期是
:vue实例从创建到销毁,也就是从开始创建、初始化数据、编译模板、挂载Dom->渲染、更新->渲染,卸载
等一系列程。
在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
vue生命周期钩子函数有哪些?
beforeCreate
: 在实例初始化之后
,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
created
:在实例创建完成后
被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer), 属性和方法的运算,watch/event 事件回调
。然而,挂载阶段还没开始,$el 属性目前不可见。
beforeMount
:在挂载开始之前
被调用:相关的 render 函数首次被调用。
mounted
:el
被新创建的 vm.$el
替换,并挂载到实例上去之后
调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。
beforeUpdate
:数据更新时调用
,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
updated
:由于数据更改导致的虚拟 DOM 重新渲染和打补丁
,在这之后
会调用
该钩子。
activated
:keep-alive 组件激活时调用。该钩子在服务器端渲染期间不被调用
。
deactivated
:keep-alive 组件停用时调用。该钩子在服务器端渲染期间不被调用。
beforeDestroy
:实例销毁之前调用。在这一步,实例仍然完全可用。该钩子在服务器端渲染期间不被调用。
destroyed
:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
errorCaptured(2.5.0+ 新增)
:当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。
注意:
1、mounted、updated
不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用vm.$nextTick
替换掉mounted、updated
:
updated: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been re-rendered
})
}
2、http请求建议在 created 生命周期内发出
vue生命周期图示:
vue如何监听键盘事件中的按键?
按键修饰符:
在监听键盘事件时,我们经常需要检查常见的键值。Vue允许为 v-on在监听键盘事件时添加按键修饰符:
<!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` -->
<input v-on:keyup.13="submit">
记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">
全部的按键别名:
- .enter
- .tab
- .delete (捕获“删除”和“退格”键)
- .esc
- .space
- .up
- .down
- .left
- .right
可以通过全局config.keyCodes
对象自定义按键修饰符别名
:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
系统修饰键:
2.1.0 新增
可以用如下修饰符
来实现仅在按下相应按键时才触发鼠标
或键盘事件
的监听器。
- .ctrl
- .alt
- .shift
- .meta
注意:
在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。**
例如
<!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>]
请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。如果你想要这样的行为,请为 ctrl 换用 keyCode:keyup.17。
- .exact 修饰符
2.5.0 新增
.exact修饰符允许你控制由精确的系统修饰符组合触发的事件。
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>
鼠标按钮修饰符
2.2.0 新增
- .left
- .right
- .middle
这些修饰符会限制处理函数仅响应特定的鼠标按钮。
vue更新数组时触发视图更新的方法 ’
变异方法:
Vue 包含一组观察数组的变异方法,所以它们也将会触发视图更新。这些方法如下:
- push():可向数组的末尾添加一个或多个元素,并返回新的长度。
- pop():用于删除并返回数组的最后一个元素。
- shift():用于把数组的第一个元素从其中删除,并返回第一个元素的值。
- unshift():可向数组的开头添加一个或更多元素,并返回新的长度。
- splice():向/从数组中添加/删除项目,然后返回被删除的项目。
- sort():用于对数组的元素进行排序。
- reverse():用于颠倒数组中元素的顺序。
替换数组:
例如:filter(), concat()和 slice() 。这些不会改变原始数组,但总是返回一个新数组。当使用这些非变异方法时,可以用新数组替换旧数组:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。幸运的是,事实并非如此。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的、启发式的方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。
注意事项:
由于 JavaScript 的限制,Vue 不能检测以下变动的数组:
1.当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
2.当你修改数组的长度时,例如:vm.items.length = newLength
举个例子:
var vm = new Vue({
data: {
items: [‘a’, ‘b’, ‘c’]
}
})
vm.items[1] = ‘x’ // 不是响应性的
vm.items.length = 2 // 不是响应性的
为了解决第一类问题,以下两种方式都可以实现和vm.items[indexOfItem] = newValue 相同的效果,同时也将触发状态更新:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
你也可以使用vm.$set实例方法,该方法是全局方法 Vue.set 的一个别名:
vm.$set(vm.items, indexOfItem, newValue)
为了解决第二类问题,你可以使用 splice:
vm.items.splice(newLength)
vue中对象更改检测的注意事项
由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:
var vm = new Vue({
data: {
a: 1
}
})
//vm.a
现在是响应式的
vm.b = 2
//vm.b
不是响应式的
对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value)方法向嵌套对象添加响应式属性。例如,对于:
var vm = new Vue({
data: {
userProfile: {
name: ‘Anika’
}
}
})
你可以添加一个新的 age 属性到嵌套的 userProfile对象:
Vue.set(vm.userProfile, ‘age’, 27)
你还可以使用 vm.$set实例方法,它只是全局Vue.set 的别名:
vm.$set(vm.userProfile, ‘age’, 27)
有时你可能需要为已有对象赋予多个新属性,比如使用 Object.assign()或 _.extend()。在这种情况下,你应该用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性,像这样:
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: ‘Vue Green’
})
如何解决非工程化项目,网速慢时初始化页面闪动问题?
使用v-cloak
指令,v-cloak
不需要表达式,它会在Vue
实例结束编译时从绑定的HTML元素上移除,经常和CSS的display:none
配合使用。
<div id="app" v-cloak>
{{message}}
</div>
<script>
var app = new Vue({
el:"#app",
data:{
message:"这是一段文本"
}
})
</script>
这时虽然已经加了指令v-cloak
,但其实并没有起到任何作用,当网速较慢、Vue.js 文件还没加载完时,在页面上会显示{{message}}
的字样,直到Vue
创建实例、编译模版时,DOM才会被替换,所以这个过程屏幕是有闪动的。只要加一句CSS
就可以解决这个问题了:
[v-cloak]{
display:none;
}
在一般情况下,v-cloak
是一个解决初始化慢导致页面闪动的最佳实践,对于简单的项目很实用
v-for产生的列表,如何实现active样式的切换?
通过设置当前 currentIndex 实现:
<template>
<div class="toggleClassWrap">
<ul>
<li @click="currentIndex = index" v-bind:class="{clicked: index === currentIndex}" v-for="(item, index) in desc" :key="index">
<a href="javascript:;">{{item.ctrlValue}}</a>
</li>
</ul>
</div>
</template>
<script type="text/javascript">
export default{
data () {
return {
desc:[{
ctrlValue:"test1"
},{
ctrlValue:"test2"
},{
ctrlValue:"test3"
},{
ctrlValue:"test4"
}],
currentIndex:0
}
}
}
</script>
<style type="text/css" lang="less">
.toggleClassWrap{
.clicked{
color:red;
}
}
</style>
v-model语法糖在组件上的使用
需要实现效果:如果在一个页面中我们需要引入一个弹窗组件,点击按钮 a
显示弹窗,然后点击弹窗的关闭按钮,关闭弹窗,用v-model
实现。
使用v-model
来进行双向数据绑定的时:
<input v-model="something">
仅仅是一个语法糖:
<input v-bind:value="something" v-on:input="something = $event.target.value">
所以在组件中使用的时候,相当于下面的简写:
<custom v-bind:value="something" v-on:input="something = $event.target.value"></custom>
所以要组件的v-model
生效,它必须:
- 接受一个
value
属性 - 在有新的
value
时触发input
事件
使用示例:
<template>
<div class="toggleClassWrap">
<modelVue v-if="ifShow" v-model="ifShow"></modelVue>
</div>
</template>
<script type="text/javascript">
import modelVue from '../../components/model.vue'
export default{
data () {
return {
ifShow:true,
}
},
components : {
modelVue
}
}
</script>
model.vue组件
<template>
<div id="showAlert">
<div>showAlert 内容</div>
<button class="close" @click="close">关闭</button>
</div>
</template>
<script>
export default{
props:{
value:{
type:Boolean,
default:false,
}
},
data(){
return{}
},
mounted(){
},
methods:{
close(){
this.$emit('input',false);//传值给父组件, 让父组件监听到这个变化
}
},
}
</script>
<style scoped>
.close{
background:red;
color:white;
}
</style>
vue-cli工作中如何自定义一个过滤器?
文件结构
.
├── src
│ ├── filter
│ │ ├── filter.js
│ │
│ └── main.js
└── …
过滤器们放在filter文件夹下
filter/filter.js:
这个文件主要是写了过滤器实现的方法,然后export进行导出。
function filterOne(n){
return n + 10;
}
function filterTwo(n){
return n + 5;
}
export{
filterOne,
filterTwo
}
main.js:
// 找 filter/filter.js
import * as filters from ‘./filter/filter.js’
//遍历所有导出的过滤器并添加到全局过滤器
Object.keys(filters).forEach((key) => {
Vue.filter(key, filters[key]);
})
在 .vue 组件下使用
{{test | filterOne}}
vue-cli工作中常用的过滤器
在 .vue 组件中的使用示例:
{{ string | trim(1) }} //去除所有空格,过滤器第一个参数为value
常用的过滤器,filter/filter.js
//去除空格 type 1-所有空格 2-前后空格 3-前空格 4-后空格
function trim(value, trim) {
switch (trim) {
case 1:
return value.replace(/\s+/g, "");
case 2:
return value.replace(/(^\s*)|(\s*$)/g, "");
case 3:
return value.replace(/(^\s*)/g, "");
case 4:
return value.replace(/(\s*$)/g, "");
default:
return value;
}
}
//任意格式日期处理
//使用格式:
// {{ '2018-09-14 01:05' | formaDate(yyyy-MM-dd hh:mm:ss) }}
// {{ '2018-09-14 01:05' | formaDate(yyyy-MM-dd) }}
// {{ '2018-09-14 01:05' | formaDate(MM/dd) }} 等
function formaDate(value, fmt) {
var date = new Date(value);
var o = {
"M+": date.getMonth() + 1, //月份
"d+": date.getDate(), //日
"h+": date.getHours(), //小时
"m+": date.getMinutes(), //分
"s+": date.getSeconds(), //秒
"w+": date.getDay(), //星期
"q+": Math.floor((date.getMonth() + 3) / 3), //季度
"S": date.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o) {
if(k === 'w+') {
if(o[k] === 0) {
fmt = fmt.replace('w', '周日');
}else if(o[k] === 1) {
fmt = fmt.replace('w', '周一');
}else if(o[k] === 2) {
fmt = fmt.replace('w', '周二');
}else if(o[k] === 3) {
fmt = fmt.replace('w', '周三');
}else if(o[k] === 4) {
fmt = fmt.replace('w', '周四');
}else if(o[k] === 5) {
fmt = fmt.replace('w', '周五');
}else if(o[k] === 6) {
fmt = fmt.replace('w', '周六');
}
}else if (new RegExp("(" + k + ")").test(fmt)) {
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
}
}
return fmt;
}
//字母大小写切换
/*type
1:首字母大写
2:首页母小写
3:大小写转换
4:全部大写
5:全部小写
* */
function changeCase(str, type) {
function ToggleCase(str) {
var itemText = ""
str.split("").forEach(
function (item) {
if (/^([a-z]+)/.test(item)) {
itemText += item.toUpperCase();
} else if (/^([A-Z]+)/.test(item)) {
itemText += item.toLowerCase();
} else {
itemText += item;
}
});
return itemText;
}
switch (type) {
case 1:
return str.replace(/\b\w+\b/g, function (word) {
return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
});
case 2:
return str.replace(/\b\w+\b/g, function (word) {
return word.substring(0, 1).toLowerCase() + word.substring(1).toUpperCase();
});
case 3:
return ToggleCase(str);
case 4:
return str.toUpperCase();
case 5:
return str.toLowerCase();
default:
return str;
}
}
//字符串循环复制,count->次数
function repeatStr(str, count) {
var text = '';
for (var i = 0; i < count; i++) {
text += str;
}
return text;
}
//字符串替换
function replaceAll(str, AFindText, ARepText) {
raRegExp = new RegExp(AFindText, "g");
return str.replace(raRegExp, ARepText);
}
//字符替换*,隐藏手机号或者身份证号等
//replaceStr(字符串,字符格式, 替换方式,替换的字符(默认*))
//ecDo.replaceStr('18819322663',[3,5,3],0)
//result:188*****663
//ecDo.replaceStr('asdasdasdaa',[3,5,3],1)
//result:***asdas***
//ecDo.replaceStr('1asd88465asdwqe3',[5],0)
//result:*****8465asdwqe3
//ecDo.replaceStr('1asd88465asdwqe3',[5],1,'+')
//result:"1asd88465as+++++"
function replaceStr(str, regArr, type, ARepText) {
var regtext = '',
Reg = null,
replaceText = ARepText || '*';
//repeatStr是在上面定义过的(字符串循环复制),大家注意哦
if (regArr.length === 3 && type === 0) {
regtext = '(\\w{' + regArr[0] + '})\\w{' + regArr[1] + '}(\\w{' + regArr[2] + '})'
Reg = new RegExp(regtext);
var replaceCount = this.repeatStr(replaceText, regArr[1]);
return str.replace(Reg, '$1' + replaceCount + '$2')
}
else if (regArr.length === 3 && type === 1) {
regtext = '\\w{' + regArr[0] + '}(\\w{' + regArr[1] + '})\\w{' + regArr[2] + '}'
Reg = new RegExp(regtext);
var replaceCount1 = this.repeatStr(replaceText, regArr[0]);
var replaceCount2 = this.repeatStr(replaceText, regArr[2]);
return str.replace(Reg, replaceCount1 + '$1' + replaceCount2)
}
else if (regArr.length === 1 && type === 0) {
regtext = '(^\\w{' + regArr[0] + '})'
Reg = new RegExp(regtext);
var replaceCount = this.repeatStr(replaceText, regArr[0]);
return str.replace(Reg, replaceCount)
}
else if (regArr.length === 1 && type === 1) {
regtext = '(\\w{' + regArr[0] + '}$)'
Reg = new RegExp(regtext);
var replaceCount = this.repeatStr(replaceText, regArr[0]);
return str.replace(Reg, replaceCount)
}
}
//格式化处理字符串
//ecDo.formatText('1234asda567asd890')
//result:"12,34a,sda,567,asd,890"
//ecDo.formatText('1234asda567asd890',4,' ')
//result:"1 234a sda5 67as d890"
//ecDo.formatText('1234asda567asd890',4,'-')
//result:"1-234a-sda5-67as-d890"
function formatText(str, size, delimiter) {
var _size = size || 3, _delimiter = delimiter || ',';
var regText = '\\B(?=(\\w{' + _size + '})+(?!\\w))';
var reg = new RegExp(regText, 'g');
return str.replace(reg, _delimiter);
}
//现金额大写转换函数
//ecDo.upDigit(168752632)
//result:"人民币壹亿陆仟捌佰柒拾伍万贰仟陆佰叁拾贰元整"
//ecDo.upDigit(1682)
//result:"人民币壹仟陆佰捌拾贰元整"
//ecDo.upDigit(-1693)
//result:"欠人民币壹仟陆佰玖拾叁元整"
function upDigit(n) {
var fraction = ['角', '分', '厘'];
var digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
var unit = [
['元', '万', '亿'],
['', '拾', '佰', '仟']
];
var head = n < 0 ? '欠人民币' : '人民币';
n = Math.abs(n);
var s = '';
for (var i = 0; i < fraction.length; i++) {
s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, '');
}
s = s || '整';
n = Math.floor(n);
for (var i = 0; i < unit[0].length && n > 0; i++) {
var p = '';
for (var j = 0; j < unit[1].length && n > 0; j++) {
p = digit[n % 10] + unit[1][j] + p;
n = Math.floor(n / 10);
}
s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s;
//s = p + unit[0][i] + s;
}
return head + s.replace(/(零.)*零元/, '元').replace(/(零.)+/g, '零').replace(/^整$/, '零元整');
}
//保留2位小数
function toDecimal2(x){
var f = parseFloat(x);
if (isNaN(f)) {
return false;
}
var f = Math.round(x * 100) / 100;
var s = f.toString();
var rs = s.indexOf('.');
if (rs < 0) {
rs = s.length;
s += '.';
}
while (s.length <= rs + 2) {
s += '0';
}
return s;
}
export{
trim,
changeCase,
repeatStr,
replaceAll,
replaceStr,
checkPwd,
formatText,
upDigit,
toDecimal2,
formaDate
}
vue等单页面应用及其优缺点
单页Web应用(single page web application,SPA):
就是只有一张Web页面的应用。单页应用程序 (SPA) 是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。浏览器一开始会加载必需的HTML、CSS和JavaScript,所有的操作都在这张页面上完成,都由JavaScript来控制。因此,对单页应用来说模块化的开发和设计显得相当重要。
单页Web应用的优点:
1、提供了更加吸引人的用户体验:具有桌面应用的即时性、网站的可移植性和可访问性。
2、单页应用的内容的改变不需要重新加载整个页面,web应用更具响应性和更令人着迷。
3、单页应用没有页面之间的切换,就不会出现“白屏现象”,也不会出现假死并有“闪烁”现象
4、单页应用相对服务器压力小,服务器只用出数据就可以,不用管展示逻辑和页面合成,吞吐能力会提高几倍。
5、良好的前后端分离。后端不再负责模板渲染、输出页面工作,后端API通用化,即同一套后端程序代码,不用修改就可以用于Web界面、手机、平板等多种客户端。
单页Web应用的缺点:
1、首次加载耗时比较多。
2、SEO问题,不利于百度,360等搜索引擎收录。
3、容易造成Css命名冲突。
4、前进、后退、地址栏、书签等,都需要程序进行管理,页面的复杂度很高,需要一定的技能水平和开发成本高。
什么是vue的计算属性?
计算属性:
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。
所以,对于任何复杂逻辑,都应当使用计算属性。
例子:
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
<script>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
</script>
结果:
Original message: "Hello"
Computed reversed message: "olleH"
vue-cli提供的几种脚手架模板
在使用之前,可以先用 vue-list命令查询可用的模板。
详解:
vue-cli提供了的常用的脚手架模板:
webpack
:基于 webpack 和 vue-loader 的目录结构,而且支持热部署、代码检查、测试及 css 抽取。
webpack-simple
:基于 webpack 和 vue-loader 的目录结构。
browerify
:基于 Browerfiy 和 vueify(作用于 vue-loader 类似)的结构,支持热部署、代码检查及单元测试。
browerify-simple
:基于 Browerfiy 和 vueify 的结构。
simple
:单个引入 Vue.js 的 index.html 页面。
这里我们主要会使用 webpack 作为常用脚手架,可以运行vue init webpack my-project
来生成项目。
vue父组件如何向子组件中传递数据?
可以分为 静态传递 或者 使用 v-bind动态传递:
例如:
给 prop 传入一个静态的值:
<blog-post title="My journey with Vue"></blog-post>
也知道 prop 可以通过 v-bind 动态赋值,例如:
<!-- 动态赋予一个变量的值 -->
<blog-post v-bind:title="post.title"></blog-post>
<!-- 动态赋予一个复杂表达式的值 -->
<blog-post v-bind:title="post.title + ' by ' + post.author.name"></blog-post>
也知道 prop 可以通过 v-bind 动态赋值,例如
<!-- 动态赋予一个变量的值 -->
<blog-post v-bind:title="post.title"></blog-post>
<!-- 动态赋予一个复杂表达式的值 -->
<blog-post v-bind:title="post.title + ' by ' + post.author.name"></blog-post>
在上述两个示例中,我们传入的值都是字符串类型的,但实际上任何类型的值都可以传给一个 prop。子组件接受:
export default {
props : ["title"]
}
//或者
export default {
props : {
title:{
type:string,
default:""
}
}
}
详解:
传入一个数字:
<!-- 即便 `42` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:likes="42"></blog-post>
<!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:likes="post.likes"></blog-post>
传入一个布尔值:
<!-- 包含该 prop 没有值的情况在内,都意味着 `true`。-->
<blog-post is-published></blog-post>
<!-- 即便 `false` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:is-published="false"></blog-post>
<!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:is-published="post.isPublished"></blog-post>
传入一个数组:
<!-- 即便数组是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>
<!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:comment-ids="post.commentIds"></blog-post>
传入一个对象:
<!-- 即便对象是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:author="{ name: 'Veronica', company: 'Veridian Dynamics' }"></blog-post>
<!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:author="post.author"></blog-post>
传入一个对象的所有属性:
如果你想要将一个对象的所有属性都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)。
例如,对于一个给定的对象 post:
post: {
id: 1,
title: 'My Journey with Vue'
}
下面的模板:
<blog-post v-bind="post"></blog-post>
等价于:
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
如何在组件中使用全局常量
第一步,在 src 下新建 const 文件夹下 新建 const.js
.
├── src
│ ├── const
│ │ ├── const.js
│ │
│ └── main.js
└── …
第二步,如何在 const.js 文件下,设置常量
export default {
install(Vue,options){
Vue.prototype.global = {
title:'全局',
isBack: true,
isAdd: false,
};
}
}
第三步,在 main.js 下全局引入:
//引入全局常量
import constant from './const/const.js'
Vue.use(constant);
第四步,即可在 .vue 组件中使用:
//通过js方式使用:
this.global.title
//或在 html 结构中使用
{{global.title}}
在 JS 中使用全局常量
第一步,在 src 下新建 const 文件夹下 新建 type.js
.
├── src
│ ├── const
│ │ ├── type.js
│ │
│ └── main.js
└── …
第二步,在 type.js 文件下,设置常量
export const TEST_INCREMENT=‘TEST_INCREMENT’
export const TEST_DEREMENT=‘TEST_DEREMENT’
第三步,在其他 .js 文件下引入并使用:
//以对象的形式引入:
import * as types from '../types'
//使用:
types.TEST_INCREMENT
如何定义一个常量,允许项目打包后,修改 [hash].js 里面的值?
第一步,在 static 下新建 config.js:
.
├── 项目路径
│ ├── static
│ │___├── config.js
第二步,在 config.js 里面设置全局变量:
window.g = {
PUBLIC_IP : "http://10.10.10.10:8080"
}
第三步,在 index.html 里面引入:
<script type="text/javascript" src="./static/config.js"></script>
第四步,在其他 .js 文件中即可使用
window.g.PUBLIC_IP
第五步,打包后修改:
通过 npm run build
命令打包后,此 config.js 文件会被打包到 dist/static文件夹下,
此时如果需要修改 PUBLIC_IP,打开config.js即可修改,无需重新打包!
vue如何禁止弹窗后面的滚动条滚动?
methods : {
//禁止滚动
stop(){
var mo=function(e){e.preventDefault();};
document.body.style.overflow='hidden';
document.addEventListener("touchmove",mo,false);//禁止页面滑动
},
/***取消滑动限制***/
move(){
var mo=function(e){e.preventDefault();};
document.body.style.overflow='';//出现滚动条
document.removeEventListener("touchmove",mo,false);
}
}
请说出计算属性的缓存和方法调用的有什么区别?
- 计算属性必须返回结果
- 计算属性是基于它的依赖缓存的。一个计算属性所依赖的数据发生变化时,它才会重新取值。
- 使用计算属性还是
methods
取决于是否需要缓存,当遍历大数组和做大量计算时,应当使用计算属性,除非你不希望得到缓存。 - 计算属性是根据依赖自动执行的,methods需要事件调用。
什么是vue.js中的自定义指令?
自定义一些指令对底层DOM进行操作
Vue里面有许多内置的指令,比如v-if和v-show,这些丰富的指令能满足我们的绝大部分业务需求,不过在需要一些特殊功能时,我们仍然希望对DOM进行底层的操作,这时就要用到自定义指令。
自定义指令的几个钩子函数
- bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
- inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
- update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 。
- componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
- unbind:只调用一次,指令与元素解绑时调用。
钩子函数参数
除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行
指令钩子函数会被传入以下参数:
el:指令所绑定的元素,可以用来直接操作 DOM 。
-
binding:一个对象,包含以下属性:
name:指令名,不包括 v- 前缀。 value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为2。 oldValue:指令绑定的前一个值,仅在update和 componentUpdated钩子中可用。无论值是否改变都可用。 expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。 arg:传给指令的参数,可选。例如 v-my-directive:foo中,参数为 "foo"。 modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为{ foo: true, bar: true }。
-
vnode:Vue 编译生成的虚拟节点。
-
oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
如何在vue-cli中使用自定义指令?
文件结构:
.
├── src
│ ├── directives
│ │ ├── index.js
│ │ ├── modules
│ └── main.js
└── …
在modules下新建foucs.js下
// 聚焦指令
export default {
bind (el, binding, vnode) {},
inserted (el, binding, vnode) {
el.focus()
},
update (el, binding, vnode) {},
componentUpdated (el, binding, vnode) {},
unbind (el, binding, vnode) {}
}
在src/directives/index.js
下
import focus from './modules/focus'
export {focus}
在src/main.js下,使用directives自定义指令
//引入自定义指令
import * as directives from './directives'
//注册指令
Object.keys(directives).forEach(k => Vue.directive(k, directives[k]));
在.vue组件中使用
<input v-focus type="text" />
父组件异步获取动态数据传递给子组件
例如,在一个index.vue
组件里面有一个content.vue
子组件,需要在http
请求数据返回后,将list
数据传递给content.vue
子组件。
利用v-if
可在http请求返回后
再显示。这样子组件可以返回的http
请求数据。
<template>
<div class="indexWrap">
<content :list="list" v-if="isBack"></content>
</div>
</template>
<script type="text/javascript">
import content from '../components/content.vue'
export default{
data () {
return {
index:0,
list:"",
isBack:false
}
},
components : {
content
},
}
</script>
更多推荐
所有评论(0)