Vue是一套构建用户界面的渐进式框架。Vue只关注视图层,采用自底向上增量开发的设计。Vue的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。
学习vue之前主要掌握的知识:HTML、CSS、JavaScript、TypeScript
兼容性说明:Vue 不支持 IE8 及其以下版本,因为 Vue 使用了 IE8 不能模拟的 ECMAScript 5 特性。但它支持所有兼容 ECMAScript 5 的浏览器。
官网地址下载安装 Download | Node.js
npm是随同NodeJS一起安装的包管理工具,安装nodejs后,就无需安装,但是npm速度会慢一些,推荐使用淘宝镜像及其命令cnpm,使用如下命令安装淘宝镜像
npm install -g cnpm --registry=https://registry.npmmirror.com
cnpm -v 查看安装的淘宝镜像版本
执行如下命令,安装vue的最新稳定版本
npm install vue
安装指定版本的vue
npm install vue@版本号
比如要安装2.6.12版本的vue,执行命令 npm install [email protected]
npm install --global vue-cli
详细使用说明参见:介绍 | Vue CLI
cmd cd命令到需要创建项目的目录下,执行 vue create 项目名称 创建vue项目
在F磁盘下myVue文件夹下创建项目名称为bookmanage
注:项目名称不能包含大写字母
使用VSCode打开项目
执行命令npm run serve,运行项目
文件目录说明
在一个 Vue CLI 项目中,@vue/cli-service 安装了一个名为 vue-cli-service 的命令。你可以在 npm scripts 中以 vue-cli-service、或者从终端中以 ./node_modules/.bin/vue-cli-service 访问这个命令。
这是你使用默认 preset 的项目的 package.json:
你可以通过 npm 或 Yarn 调用这些 script:
npm run serve
# OR
yarn serve
用法:vue-cli-service serve [options] [entry]
选项:
--open 在服务器启动时打开浏览器
--copy 在服务器启动时将 URL 复制到剪切版
--mode 指定环境模式 (默认值:development)
--host 指定 host (默认值:0.0.0.0)
--port 指定 port (默认值:8080)
--https 使用 https (默认值:false)
vue-cli-service serve 命令会启动一个开发服务器 (基于 webpack-dev-server) 并附带开箱即用的模块热重载 (Hot-Module-Replacement)。
除了通过命令行参数,你也可以使用 vue.config.js 里的 devServer 字段配置开发服务器。
命令行参数 [entry] 将被指定为唯一入口 (默认值:src/main.js,TypeScript 项目则为 src/main.ts),而非额外的追加入口。尝试使用 [entry] 覆盖 config.pages 中的 entry 将可能引发错误
用法:vue-cli-service build [options] [entry|pattern]
选项:
--mode 指定环境模式 (默认值:production)
--dest 指定输出目录 (默认值:dist)
--modern 面向现代浏览器带自动回退地构建应用
--target app | lib | wc | wc-async (默认值:app)
--name 库或 Web Components 模式下的名字 (默认值:package.json 中的 "name" 字段或入口文件名)
--no-clean 在构建项目之前不清除目标目录的内容
--report 生成 report.html 以帮助分析包内容
--report-json 生成 report.json 以帮助分析包内容
--watch 监听文件变化
vue-cli-service build 会在 dist/ 目录产生一个可用于生产环境的包,带有 JS/CSS/HTML 的压缩,和为更好的缓存而做的自动的 vendor chunk splitting。它的 chunk manifest 会内联在 HTML 里。
这里还有一些有用的命令参数:
用法:vue-cli-service inspect [options] [...paths]
选项:
--mode 指定环境模式 (默认值:development)
你可以使用 vue-cli-service inspect 来审查一个 Vue CLI 项目的 webpack config。
vue.config.js是vue项目的配置文件,专注于vue项目。使用脚手架安装项目的时候并没有创建vue.config.js,所有一般是需要修改webpack的时候才会自己创建一个vue.config.js,然后就可以修改默认的webpack。
配置说明参见:https://cli.vuejs.org/zh/guide/webpack.html
module.exports ={
lintOnSave: true, // 关闭eslint 默认是true
productionSourceMap: false, // 不需要生产时的源映射(即.js.map文件不会生成) 默认是true
// 部署应用包时的基本URL。 默认是/ 。如应用被部署在 https://www.my-app.com/my-app/,则设置publicPath为/my-app/
publicPath: process.env.NODE_ENV === 'production' ? '/' : '/', // ./是当前目录 ; /是根目录
outputDir: 'dist', // 默认dist 所以这句可以注释掉
assetsDir: "static",// 项目打包的静态资源存放目录,默认 "static"
indexPath: "index.html",// 项目打包的index.html输出路径,默认 "index.html"
pages: undefined,// 多页应用配置参数,默认 undefined
// 配置代理跨域
devServer: {
open: true, //启动项目自动弹出浏览器
port: 5200, // 开发运行时的端口
https: false, // 是否启用 https
host: '0.0.0.0', // 默认localhost 设置成'0.0.0.0',在同一个局域网下,可以通过http://ip:port/...访问项目
proxy: { // 拦截所有api开头的请求,请求时删除api(根据自己实际情况来)
"^/api/.*": {
target: 'http://127.0.0.1:8090', // 服务器接口地址
changeOrigin: true, // 是否跨域
rewrite: (path) => path.replace(/^\/api/, ''),
}
}
},
// css全局配置 参见 https://cli.vuejs.org/zh/guide/css.html#css-modules
//特别说明:配置前先安装好需要的依赖;如:“sass-loader”: “^10.2.0”, “scss”: “^0.2.4”,安装的如果版本不一致会有报错 参见 https://www.npmjs.com/package/node-sass 查看nodejs与node-sass对应版本
//如果使用scss/sass设置,那么需要在每个vue文件中,需要这样写 全局样式才会生效
/**
* sass-loader与node-sass对应版本号
* sass-loader 4.1.1,node-sass 4.3.0
sass-loader 7.0.3,node-sass 4.7.2
sass-loader 7.3.1,node-sass 4.7.2
sass-loader 7.3.1,node-sass 4.14.1
sass-loader 10.0.1,node-sass 6.0.1
*/
css: {
// 是否将组件中的 CSS 提取至一个独立的 CSS 文件中、生产环境下默认 true,开发环境下默认 false
extract: true,
// 是否开启 css 的 source map 调试,默认 false
sourceMap: false,
//向 CSS 相关的 loader 传递选项(支持 css-loader postcss-loader sass-loader less-loader stylus-loader) 默认{}
loaderOptions: {
scss:{
// v8 以前的写法 data: `@import @/assets/css/index.scss;`,
// v8 以后的写法prependData: `@import @/assets/css/index.scss;`,
// v10 以后的写法
additionalData: `@import "@/assets/css/common.scss";`// v8中,这个选项名是 "prependData"
},
// sass:{
// additionalData: `@import "@/assets/css/common.scss";`// v8中,这个选项名是 "prependData"
// }
}
},
//新增/修改 webpack 的 plugins 或者rules 的简单配置方式使用configureWebpack
//参见 https://cli.vuejs.org/zh/guide/webpack.html
configureWebpack:config=>{
console.log(config);
if(process.env.NODE_ENV==="development"){
//开发换进改配置
}
else if(process.env.NODE_ENV==="test"){
//测试环境配置
}
else if(process.env.NODE_ENV==="production"){
//生产环境配置
}
},
//新增/修改 webpack 的 plugins 或者rules 的链式操作 (高级)方式使用chainWebpack
//参见 https://cli.vuejs.org/zh/guide/webpack.html
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.tap(options => {
// 修改它的选项...
return options;
});
}
}
模式
模式是 Vue CLI 项目中一个重要的概念。默认情况下,一个 Vue CLI 项目有三个模式:
你可以通过传递 --mode 选项参数为命令行覆写默认的模式。例如,如果你想要在构建命令中使用开发环境变量:
vue-cli-service build --mode development
当运行 vue-cli-service 命令时,所有的环境变量都从对应的环境文件中载入。如果文件内部不包含 NODE_ENV 变量,它的值将取决于模式,例如,在 production 模式下被设置为 "production",在 test 模式下被设置为 "test",默认则是 "development"。
NODE_ENV 将决定您的应用运行的模式,是开发,生产还是测试,因此也决定了创建哪种 webpack 配置。
例如通过将 NODE_ENV 设置为 "test",Vue CLI 会创建一个优化过后的,并且旨在用于单元测试的 webpack 配置,它并不会处理图片以及一些对单元测试非必需的其他资源。
同理,NODE_ENV=development 创建一个 webpack 配置,该配置启用热更新,不会对资源进行 hash 也不会打出 vendor bundles,目的是为了在开发的时候能够快速重新构建。
当你运行 vue-cli-service build 命令时,无论你要部署到哪个环境,应该始终把 NODE_ENV 设置为 "production" 来获取可用于部署的应用程序。
NODE_ENV
如果在环境中有默认的 NODE_ENV,你应该移除它或在运行 vue-cli-service 命令的时候明确地设置 NODE_ENV。
环境变量
你可以在你的项目根目录中放置下列文件来指定环境变量:
.env # 在所有的环境中被载入
.env.local # 在所有的环境中被载入,但会被 git 忽略
.env.[mode] # 只在指定的模式中被载入
.env.[mode].local # 只在指定的模式中被载入,但会被 git 忽略
一个环境文件只包含环境变量的“键=值”对:
FOO=bar
VUE_APP_NOT_SECRET_CODE=some_value
警告
不要在你的应用程序中存储任何机密信息(例如私有 API 密钥)!
环境变量会随着构建打包嵌入到输出代码,意味着任何人都有机会能够看到它。
请注意,只有 NODE_ENV,BASE_URL 和以 VUE_APP_ 开头的变量将通过 webpack.DefinePlugin 静态地嵌入到客户端侧的代码中。这是为了避免意外公开机器上可能具有相同名称的私钥。
想要了解解析环境文件规则的细节,请参考 dotenv。我们也使用 dotenv-expand 来实现变量扩展 (Vue CLI 3.5+ 支持)。例如:
FOO=foo
BAR=barCONCAT=$FOO$BAR # CONCAT=foobar
被载入的变量将会对 vue-cli-service 的所有命令、插件和依赖可用。
环境文件加载优先级
为一个特定模式准备的环境文件 (例如 .env.production) 将会比一般的环境文件 (例如 .env) 拥有更高的优先级。
此外,Vue CLI 启动时已经存在的环境变量拥有最高优先级,并不会被 .env 文件覆写。
.env 环境文件是通过运行 vue-cli-service 命令载入的,因此环境文件发生变化,你需要重启服务。
模式和环境变量配置示例
在实际开发过程中我们可能需要开发环境、测试环境和生产环境对应三个不同环境的地址,这里我们在项目根目录中新增.env、.env.development、.env.test、.env.production三个文件,分别对应所有环境、开发、测试、生产不同模式下的环境变量配置
.env
NODE_ENV=development
VUE_APP_API_URL= 'http://127.0.0.1:8080/' // 开发接口域
.env.development
NODE_ENV=development
VUE_APP_API_URL= 'http://127.0.0.1:8080/' // 开发接口域
.env.test
NODE_ENV=test
VUE_APP_API_URL= 'http://127.0.0.1:8089/' // 开发接口域
.env.production
NODE_ENV=production
VUE_APP_API_URL= 'http://127.0.0.1:9095/' // 开发接口域
然后在package.json中的scrips中配置service命令
分别执行 npm run serve 和npm run test命令,页面中显示process.env.NODE_ENV值的效果
Vue路由是指根据url分配到对应的处理程序;作用就是解析URL,调用对应的控制器的方法,并传递参数。Vue路由有助于在浏览器的URL或历史记录与Vue组件之间建立链接,从而允许某些路径渲染与之关联的任何一个视图。
$route:一般获取路由信息【路径、query、params等等】
$router:一般进行编程式导航进行路由跳转【push|replace】
项目中创建router.js文件,创建路径src/router/router.js
import Vue from 'vue';//引入vue
import VueRouter from 'vue-router';//引入vue-router
Vue.use(VueRouter)//第三方库需要use一下才能用
//定义routes路由的集合,数组类型
//路由懒加载: 当打包构建应用时,JavaScript包会变得非常大,影响页面加载。如果能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件。
const routes = [
//路由配置
// { path: '/login', component: ()=> import('@/views/login/index.vue')},
// { path: '/index', component: ()=> import('@/views/home/index.vue')}
];
//实例化 VueRouter 并将 routes 添加进去
const router = new VueRouter({
//ES6简写,等于routes:routes
routes
});
//抛出这个这个实例对象方便外部读取以及访问
export default router;
然后在main.js中引入路由js文件
import Vue from 'vue'
import App from './App.vue'
import router from './router/router.js'//引用router.js
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App),
}).$mount('#app')
样式文件包含sass、scss、css、less等,全局样式的设置方式如下:
1)main.js中引入
import '@/assets/css/common.scss' // 全局样式
2)vue.config.js 中配置
详情参见 1.3.3.1 vue.config.js中的关于全局css配置的相关注释说明及代码
每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的:
import Vue from 'vue'//引入vue
var vm = new Vue({
// 选项
})
选项参数包含根组件、路由等的配置,我们可以在入口程序main.js中的代码中看出,在vue实例中调用根组件App.vue,根组件中调用子组件
main.js中的代码:
根组件App.Vue中的代码:
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数
挂载阶段:beforeCreate、created、beforeMount、mounted
更新阶段:beforeUpdate、updated
销毁阶段:beforeDestroy、destroyed
钩子函数常见的使用场景
1)推荐在created()中发送请求,对服务端接口进行调用,此时data数据已经创建,可以获取服务端数据对其进行赋值,相比beforeMount()和mounted(),created()能更快获取到服务端数据,减少页面loading 时间;
2)在 mounted()中,Vue将编译好的模板挂在到页面上,此时为真实DOM,可以对DOM进行操作。此时可以做启动定时器、绑定自定义事件、订阅消息等初始化操作。
3)beforeDestory()可进行清除定时器、解绑自定义事件、取消消息订阅等操作
Vue 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。
Vue的核心是一个允许你采用简洁的模板语法来声明式的将数据渲染进 DOM 的系统。
结合响应系统,在应用状态改变时, Vue 能够智能地计算出重新渲染组件的最小代价并应用到 DOM 操作上。
数据绑定最常见的形式就是使用 {{...}}(双大括号)的文本插值
通过使用v-once指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定
{{title}}
{{titleOnce}}
页面输出效果:
双大括号将会将数据插值为纯文本,而不是 HTML。若想插入 HTML,你需要使用v-html指令
HTML
页面输出效果:
双大括号不能在 HTML attributes 中使用,相应的,应该使用v-bind指令
注:v-bind可省略不写,直接:属性值,比如绑定id可以这样写:id="dynamicId"
动态绑定多个值
对于所有的数据绑定,Vue都提供了完全的 JavaScript 表达式支持
在 Vue 模板内,JavaScript 表达式可以被使用在如下场景上:
表达式会被作为 JavaScript ,以组件为作用域,解析执行
当前数值是:{{num+1}}
当前状态是否ok:{{isOk?'是':'否'}}
姓氏:{{message.split(',').join(' ')}}
动态绑定id
运行输出结果:
在绑定的表达式中,可以使用一个组件暴露的方法,调用函数
绑定在表达式中的方法,在组件每次更新时,都会被重新调用,因此不应该产生任何effect(副作用)。比如改变数据或触发异步操作。
{{dateToStr()}}
运行效果:
注意事项
1)在绑定的表达式中,不支持语句 ,以下写法都是无效的
{{ var a = 1 }} {{ if (ok) { return message } }}
2)受限的全局访问
模板中的表达式将被沙盒化,仅能够访问到有限的全局对象列表。该列表中,会暴露常用的内置全局对象,比如Math和Date。
没有显式包含在列表中的全局对象,将不能在模板内表达式中访问。例如,用户附加在window上的 property。然而,你也可以自行在app.config.globalProperties上显式地添加他们,供所有的 Vue 表达式使用。
指令是带有v-前缀的特殊 attribute。指令 attribute 的期望值,为一个 JavaScript 表达式(v-for、v-on和v-slot将会是例外)。使用指令是为了,在其表达式值变化时,响应式的更新 DOM。
以v-if为例:
v-if指令
一些指令能够接收一个“参数”,在指令名称之后以:
(冒号)隔开,做标识。例如,v-bind指令可以用于响应式地更新 HTML attribute:
...
...
这里href
就是一个参数,它告诉v-bind指令,将表达式url的值,绑定到元素的href
attribute 上。在缩写中,参数前的一切(例如v-bind:
)都会被缩略为一个:
字符。
另一个例子是v-on指令,它用于监听 DOM 事件:
...
...
这里的参数是要监听的事件名称:click
。v-on
也是少部分含有缩写的指令之一,缩写字符为@
从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:
这里的attributeName会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的组件实例有一个data property attributeName,其值为"href",那么这个绑定将等价于v-bind:href。
同样地,你可以使用动态参数为一个动态的事件名绑定处理函数:
...
在这个示例中,当eventName的值为"focus"时,v-on:[eventName]将等价于v-on:focus
动态参数值限制:动态参数预期会求出一个字符串,异常情况下值为 null
。这个特殊的 null
值可以被显性地用于移除绑定。任何其它非字符串类型的值都将会触发一个警告
动态参数表达式限制
动态参数表达式因为某些字符的缘故有一些语法限制,比如空格和引号,在 HTML attribute 名称中都是不合法的。例如下面的示例:
如果你需要传入一个复杂的动态参数,我们推荐使用计算属性替换复杂的表达式
当使用DOM 内嵌模板(直接写在 HTML 文件里的模板)时,我们需要避免在名称中使用大写字母,因为浏览器会强制将其转换为小写:
修饰符 (modifier) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():
vue允许你自定义过滤器,被用作一些常见的文本格式化。由"管道符"指示, 格式如下:
{{ message | capitalize }}
过滤器函数接受表达式的值作为第一个参数
以下示例代码中,将字符串转换为大写字符:
{{message|toUp}}
过滤器可以串联:
{{ message | filterA | filterB }}
过滤器是 JavaScript 函数,因此可以接受参数:{{ message | filterA('arg1', arg2) }}
这里,message 是第一个参数,字符串 'arg1' 将传给过滤器作为第二个参数, arg2 表达式的值将被求值然后传给过滤器作为第三个参数。
数据绑定的一个常见需求场景是操纵元素的CSS类列表和内联样式。因为它们都是attribute,我们可以使用v-bind来做这件事:我们只需要通过表达式计算出一个字符串作为最终结果即可。然而频繁操作字符串连接很闹心,也很容易出错的。因此 Vue 为class和style的v-bind使用提供了特殊的功能增强。除了字符串外,表达式的结果还可以是对象或数组。
根据字段值true/false绑定calss
多字段来动态切换多个class
绑定数据对象来动态切换多个class
通过计算属性来动态切换多个class
渲染效果:
绑定数组的方式动态切换多个class
根据条件来动态切换多个class
数组中使用对象的方式来动态切换多个class
渲染效果:
当在一个自定义组件上使用 class property 时,这些 class 将被添加到该组件的根元素上面。这个元素上已经存在的 class 不会被覆盖
比如使用HelloWorld的组件,v-bind:class方式同样适用
v-bind:style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名
对象方式设置内联样式
样式对象设置内联样式
计算属性设置内联样式
渲染效果:
内联方式的数组是数组对象
数组方式设置内联样式
渲染效果:
当你在:style中使用了需要浏览器特殊前缀的 CSS 属性时,Vue 会自动为他们加上相应的前缀。Vue 是在运行时检查该属性是否支持在当前浏览器中使用。如果浏览器不支持某个属性,那么将测试加上各个浏览器特殊前缀,以找到哪一个是被支持的
如 transform
从 2.3.0 起你可以为 style 绑定中的 property 提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:
这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex
条件语句包含:
注意:不推荐同时使用 v-if 和 v-for,当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。
大家好,我是超级管理员
大家好,我普通用户
大家好,我管理员角色
大家好,我超级管理员角色
大家好,我主管角色
大家好,我是普通用户角色
渲染效果:
另一个用于根据条件展示元素的选项是 v-show 指令
不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中,v-show 只是简单地切换元素的 CSS property display。
注意:v-show 不支持 元素,也不支持 v-else。
大家好,我是超级管理员
大家好,我是普通用户
大家好,我是管理员角色
大家好,我是超级管理员角色
大家好,我是主管角色
大家好,我是普通用户角色
渲染效果:
循环语句使用v-for指令
循环语法
v-for="item in items"
v-for="item of items"
v-for 可遍历数组和对象,在 v-for
块中,我们可以访问所有父作用域的 property
遍历数组,支持可选的第二个参数(当前项的索引)
v-for="item in items"
v-for="(item,index)in items"
遍历对象支持可选的第二个参数(键名),还支持 可选的第三个参数(当前索引)
v-for="value in object"
v-for="(value,name) in object"
v-for="(value,name,index) in object"
注意:v-for 遍历时,一定要绑定元素key值,否则编译报错 :key="item"
在遍历对象时,会按 Object.keys()
的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。
v-for遍历数组
- {{item.studentName}}
v-for遍历数组——元素值和当前索引参数
- {{index}}.{{item.studentName}}
v-for遍历对象
- {{value}}
v-for遍历对象——值和键名参数
- {{name}}:{{value}}
v-for遍历对象——值、键名、当前索引参数
- {{index}}、{{name}}:{{value}}
渲染效果:
可以直接传给v-for
一个整数值。在这种用例中,将会将该模板基于1...n
的取值范围重复多次
v-for遍历整数
- {{n}}
渲染效果:
当 Vue 正在更新使用 v-for
渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
因为它是 Vue 识别节点的一个通用机制,key 并不仅与 v-for 特别关联。后面我们将在指南中看到,它还具有其它用途。
使用v-for
在 使用v-for 来循环渲染一段包含多个元素的内容。比如:
- {{ item.msg }}
组件中使用v-for
在使用组件时,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域,为了把迭代数据传递到组件里,我们要使用 prop:
v-for="(item, index) in items"
v-bind:item="item"
v-bind:index="index"
v-bind:key="item.id"
>
我们定义一个helloworld的组件,组件参数为msg
{{ msg }}
引用helloworld组件,使用v-for
渲染效果:
变更方法,会变更原始数组。Vue 能够侦听响应式数组的变更方法,并在它们被调用时,触发相关的视图更新。这些变更方法包括:
push()
:向数组的末尾添加一个或多个元素,并返回新的长度。pop()
:删除数组的最后一个元素,并返回最后一个元素。shift()
:删除数组的第一个元素,并返回第一个元素的值。unshift()
:向数组的开头添加一个或更多元素,并返回新的长度。sort()
:对数组的元素进行排序。reverse()
:颠倒数组中元素的顺序。splice()
:向/从数组中添加/删除项目,然后返回被删除的项目。
替换数组,不变更原始数组,而总是返回一个新数组。当使用非变更方法时,可以用新数组替换旧数组,触发相关的视图更新。
// `items` 是一个数组的 ref
items.value = items.value.filter((item) => item.message.match(/Foo/))
你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。幸运的是,事实并非如此。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的启发式方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。
当我们想要显示一个数组经过过滤或排序后的版本,而不实际变更或重置原始数据。在这种情况下,可以创建一个计算属性,来返回过滤或排序后的数组。
使用计算属性展示过滤或排序后的结果
- {{n}}
渲染效果:
在计算属性不适用的情况下 (例如,在嵌套 v-for 循环中) 你可以使用一个方法:
- {{ n }}
data: {
sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
},
methods: {
even: function (numbers) {
return numbers.filter(function (number) {
return number % 2 === 0
})
}
}
在计算属性中使用reverse()和sort()请保持谨慎!这两个方法将改变原始数组,计算函数中不应该这么做。请在调用这些方法之前创建一个原数组的副本:
// 慎用:
return numbers.reverse()
// 推荐:
return [...numbers].reverse()
当我们在模板中多次使用相同的表达计算方式或者计算逻辑复杂的,推荐使用计算属性(computed),来描述依赖响应式状态的复杂逻辑,计算属性会自动追踪响应式依赖。
使用计算属性可以减少代码的重复性,降低维护难度,增强代码的可读性。
使用计算属性实现反转字符串实例:
使用计算属性展实现反转字符串
{{reverseMsg}}
渲染效果:
计算属性默认只有 getter,不过在需要时你也可以提供一个 setter
计算两个数据的占比实例:
计算属性的setter
{{calPercent}}
我们可以通过在表达式中调用方法来达到和计算属性同样的效果,可以将同一函数定义为一个方法而不是一个计算属,两种方式的最终结果确实是完全相同的。
然而,不同的是计算属性是基于它们的响应式依赖进行缓存的,只在相关响应式依赖发生改变时它们才会重新求值。
相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
计算属性允许我们声明性地计算衍生值。然而,在有些情况下,为了应对一些状态的变化,我们需要运行些effect(副作用):例如更改 DOM,或者根据异步操作的结果,去修改另一处的状态。
我们可以使用watch()
函数,在每次响应式状态发生变化时,触发回调函数
以监听question变量为实例的代码:
Ask a yes/no question:
{{ answer }}
{{changeInfo}}
点击“改变question”按钮或者在文本框中输入问题,页面显示:
question变更后:
使用v-on指令(缩写为@符号)来监听 DOM 事件,并在触发事件时,执行一些 JavaScript
按钮点击事件代码示例:
或者
事件处理器的值可以是:
模板编译器,会通过检查v-on的值,是否是合法的 JavaScript 标识符或属性访问,来断定是何种形式的事件处理器。举个例子,foo、foo.bar和foo['bar']会被视为方法事件处理器,而foo()和count++会被视为内联事件处理器。
内联事件处理器的使用场景:
实例代码:
内联事件处理器—简单场景
计数:{{ count }}
内联事件处理器—调用方法
问题:{{question}}
内联事件处理器—访问事件参数
内联事件处理器—多事件处理器
随着事件处理器的逻辑变得愈发复杂,内联代码方式变得不够灵活。因此v-on也可以接受一个方法名或对某个方法的调用
实例代码:
事件处理方法
DOM 在触发事件后,会经历事件捕获和事件冒泡两个最重要阶段。在 W3C 标准中,任何事件发生时,先从顶层开始进行事件捕获,直到事件触发到达了事件源元素。然后,再从事件源往上进行事件冒泡,直到到达 document。浏览器 IE 只支持事件冒泡。
在处理事件时调用event.preventDefault()或event.stopPropagation()是很常见的。尽管我们可以直接在方法内调用,但如果方法能更专注于数据逻辑,而不用去处理 DOM 事件的细节,会更好。
为解决这一问题,Vue 为v-on提供了事件修饰符。修饰符是用.(点)表示的指令后缀。
...
...
注意:使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
.capture,.once,和.passive修饰符与原生addEventListener事件相同:
...
...
.passive修饰符一般用于触摸事件的监听器,可以用来改善移动端设备的滚屏性能。
注意:请勿同时使用.passive和.prevent,因为.prevent会被忽略并且你的浏览器可能会抛出警告。请记住,.passive是向浏览器表明你不想阻止事件的默认行为。并且如果你这样做,可能在浏览器中收到一个警告。
在监听键盘事件时,我们经常需要检查特定的按键。Vue 允许在v-on或@监听按键事件时,添加按键修饰符。
你可以直接使用KeyboardEvent.key暴露的按键名称作为修饰符,但需要转为kebab-case形式。
在上述示例中,处理函数只会在$event.key等于'PageDown'时被调用。
Vue 为最常用的键提供了别名:
注意:有一些按键 (.esc 以及所有的方向键) 在 IE9 中有不同的 key 值, 如果你想支持 IE9,这些内置的别名应该是首选。
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。
Do something
请注意修饰键与常规按键不同,在和keyup事件一起用时,事件触发时,修饰键必须处于按下状态。换句话说,keyup.ctrl只有在按住ctrl的情况下,但松开了另一个键时,才被触发。而单单释放ctrl也不会触发事件。
.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。
这些修饰符会限制处理函数仅响应特定的鼠标按钮。
在前端处理表单时,我们常常需要将表单输入框的内容同步给 JavaScript 中相应的变量。手动连接值绑定和更改事件监听器可能会很麻烦,v-model指令帮我们简化了这一步骤。
另外,v-model还可以用于各种不同类型的输入元素。它会根据所使用的元素自动扩展到不同的 DOM 属性和事件组合:
v-model会忽略所有表单元素的 attribute 的初始值(比如:value、checked、selected)。它将始终将当前绑定的 JavaScript 状态视为数据的正确来源。你应该在 JavaScript 中声明该初始值,使用响应式数据。
对于需要使用输入法 (如中文、日文、韩文等) 的语言,你会发现 v-model 不会在输入法组合文字过程中得到更新。如果你也想处理这个过程,请使用 input 事件
包含文本(text)、多行文本(textarea)、复选框(checkbox)、单选按钮(radio)、选择框(select)控件的数据绑定
代码实例:
文本绑定值:{{txtContent}}
多行文本绑定值:{{txtAreaContent}}
单个复选框
多个复选框绑定到同一个数组或集合的值
Checked names: {{ multiCheck }}
单选按钮
Picked: {{ radioVal }}
选择框
Selected: {{ selected }}
选择框多选,绑定到一个数组,多选时需要按住ctrl选择多个
Selected: {{ multiSelect }}
v-for 动态渲染绑定选择框
Selected: {{ selected }}
对于多行文本,在文本区域插值 () 并不会生效,应用 v-model 来代替。
对于选择框,如果 v-model 表达式的初始值未能匹配任何选项,
对于单选按钮,复选框及选择框的选项,v-model绑定的值通常是静态字符串(对于复选框也可以是布尔值),但有时我们可能希望将该值绑定到当前活动实例上的动态属性,那么可以使用v-bind来做到。此外使用v-bind还使我们可以将选项值,绑定为非字符串类型
复选框值绑定
单选按钮值绑定
Picked: {{ radioVal }}
选择框的值绑定
Selected: {{ selected }}
效果:
.lazy修饰符
{{msg}}
.number修饰符
{{age}}
.trim修饰符
注意:如果使用了.lazy修饰符,文本改变需要点击一下页面空白处,页面中的变量值才会同步更新
组件(Component)是 Vue 最强大的功能之一。
组件可以扩展 HTML 元素,封装可重用的代码。
组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
使用组件的步骤:
1.定义组件(创建组件实例)
2.注册组件
全局组件(component(‘组件名’,组件实例))
局部组件(components:{组件实例…})
3.使用组件(使用组件标签)
<组件名>组件名> 双标签写法
<组件名/> 单标签写法
组件命名方式:
使用 kebab-case
Vue.component('my-component-name', { /* ... */ })
当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如
使用 PascalCase
Vue.component('MyComponentName', { /* ... */ })
当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说
我们使用Vue.component来注册全局组件
方式一
Vue.component('my-component-name', {
// ... 选项 ...
})
使用此方式注册的全局组件,如果出现以下错误
You are using the runtime-only build of Vue where the template compiler is not available.
Either pre-compile the templates into render functions, or use the compiler-included build.
解决方案:
在vue.config.js配置文件中将runtimeCompiler设置为true
module.exports ={
runtimeCompiler:true
}
方式二
import 组件别名 from 'xx.vue';
Vue.component('component-a', 引入的组件别名)
我们在main.js中注册全局组件,实例代码
import Vue from 'vue'
import App from './App.vue'//引入根组件
import router from './router/router.js'//引用router.js
import '@/assets/css/common.scss' // 全局样式
import allCountCom from './components/VueAllCom.vue'//引入组件
Vue.config.productionTip = false;
//注册全局组件——button-counter
Vue.component('button-counter', {
data:function() {
return {
count: 0
}
},
template: ''
});
//通过import vue文件方式注册全局组件
Vue.component('AllCounter',allCountCom);
//实例化vue
new Vue({
router,
render: h => h(App),//导入根组件
}).$mount('#app');//mount挂载应用 根组件id
VueAllCom.vue 代码
点击了{{count}}次
使用全局组件
效果:
当组件中使用模板字符串来定义页面元素时,当控件类型有多个时,必须要在模板字符串中设置div根元素,比如:
//JavaScript 对象来定义局部组件 ButtonCounter
var ButtonCounter={
data:function(){
return {
count:0
}
},
props:{
title:String,
author:String
},
template:`
标题{{title}},作者{{author}}
`
};
全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。
局部组件的注册方式:
通过一个普通的 JavaScript 对象来定义组件,然后在components中注册
//定义组件
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
components中注册组件
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
注意: JavaScript 对象来定义组件,需要在vue.config.js中设置runtimeCompiler:true
使用import 引入vue文件,,然后在components中注册
import 组件别名 from 'xx.vue';//引入组件,局部注册
components中注册组件
components: {
组件别名
}
我们将上述全局组件中的两个组件实例转换成局部组件,代码如下:
注意局部注册的组件在其子组件中不可用。例如,如果你希望 ComponentA 在 ComponentB 中可用,则你需要这样写:
var ComponentA = { /* ... */ }
var ComponentB = {
components: {
'component-a': ComponentA
},
// ...
}如果两个子组件都是单独的vue文件,则需要在一个vue文件中,通过import的方式引入另一个子组件
import ComponentA from './ComponentA.vue'export default {
components: {
ComponentA
},
// ...
}
prop 是子组件用来接受父组件传递过来的数据的一个自定义属性。
父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明 "prop"
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
Vue.component('blog-post', {
// 在 JavaScript 中是 camelCase 的
props: ['postTitle'],
template: '{{ postTitle }}
'
})
重申一次,如果你使用字符串模板,那么这个限制就不存在了
prop类型:字符串数组、对象
/*数组类型*/
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
/*对象 每个 prop 都有指定的值类型*/
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
实际上任何类型的值都可以传给一个 prop,无需在意prop定义的数据类型
传入数字
传入布尔值
传入数组
传入对象
传入对象的所有property
post: {
id: 1,
title: 'My Journey with Vue'
}
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
这里有两种常见的试图变更一个 prop 的情形:
1.这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
2.这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。
为了定制 prop 的验证方式,你可以为 props
中的值提供一个带有验证需求的对象,而不是一个字符串数组。例如:
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].includes(value)
}
}
}
})
注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的 property (如 data
、computed
等) 在 default
或 validator
函数中是不可用的。
类型检查
type 可以是下列原生构造函数中的一个:
额外的,type 还可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认。例如,给定下列现成的构造函数:
function Person (firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
你可以使用:
Vue.component('blog-post', {
props: {
author: Person
}
})
来验证 author prop 的值是否是通过 new Person 创建的。
在我们开发组件时,组件的一些功能可能要求我们和父级组件进行沟通。
比如我们需要设置文本的字号
最简单的方式是我们在父组件中统一设置文本内容的字号,这样每个子组件中显示的字号都一致了,实例代码:
效果:
我们还可以通过监听子组件的事件,来动态事件文本字体的变化,我们需要在代码中做以下改变:
1)在子组建中添加事件 v-on:enlarge-text="postFontSize += 0.1"
2)在子组件中新增放大字体的按钮,通过调用内建的 $emit 方法并传入事件名称来触发组件的enlarge-text事件,
实例代码如下:
效果:
当我们要考虑让子组件自己决定字号的大小,那么此时我们需要做如下改变:
1)用事件来抛出一个特定的值,子组件中的按钮$emit方法添加字号固定变大值:
2)子组件的事件代码改为:v-on:enlarge-text="postFontSize += $event"
实例代码如下:
子组件事件也可通方法,完成字号放大的功能:
1)子组件事件改成调用方法onEnlargeText v-on:enlarge-text="onEnlargeText"
2)父组件methods中创建onEnlargeText 方法:
methods: {
onEnlargeText: function (enlargeAmount) {
this.postFontSize += enlargeAmount
}
}
以input的一个组件未示例,展示如何实现在组件中使用v-model
实例代码:
为了让组件正常工作,这个组件内的 必须:
将其 value attribute 绑定到一个名叫 value 的 prop 上
在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出
在prop子组件传参章节中,我们学习到了子组件可以接受 props,来接受父组件传递数据,它可以是任何类型的 JavaScript 值。
但是模板内容呢?在某些情况下,我们可能希望把模板片段传递给子组件,并让子组件在其自己的模板中渲染该片段,我们就要用到插槽了,插槽我们使用slot来进行占位
这里我们创建一个组件FilmClassify来展示分类信息,组件中写入插槽slot,
第一个在子组件组件film-classify插入模板
第二个在子组件中插入一张风景图片
实例代码如下:
- 肖申克的救赎
- 1912
- 零的执行人
渲染效果:
DOM解析会把子组件中的
具名插槽其实只是在默认插槽的基础上给每个插槽起了名字,作用为可以在组件中设置多个插槽,可以更具体细分。首先给组件插槽起名字,使用 name="xxx"。然后在子组件中插入模板时,将模板内容插入指定名称的插槽中(slot="插槽名称")
我们将默认插槽中实例的子组件插槽改成两个插槽,一个命名为films,一个命名为viewimg
修改后的代码:
- 肖申克的救赎
- 1912
- 零的执行人
渲染效果:
最终的DOM
作用域插槽简单来说就是带有数据的插槽,把组件中的数据绑定给插槽,然后谁使用这个组件谁就能拿到这个数据使用,也相当于一种数据通信,其需要这样把数据绑定定义给组件插槽(名称没有要求)
然后就要使用组件时使用 slot-scope="xxx" 去接收,或者直接使用 scope,要注意的是此处标准一点最好写在 template 中
实例代码:
- {{h}}
渲染效果:
实现动态组件,需要使用内部组件component来实现,内部组件的概念和属性:
Vue内部提供的组件component组件作用是:实现动态的渲染组件,按需显示组件。
component 标签是 vue 内置的,作用:组件的占位符
is 属性的值,表示要渲染的组件的名字
is 属性的值,应该是组件在 components 节点下的注册名称
我们会在一个多标签的界面中使用 is
attribute 来切换不同的组件,这里我们使用两个按钮来实现模拟tab动态切换的功能,实现组件的动态显示
实例代码:
动态组件
组件切换时的DOM解析:
使用
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。例如:
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 向 `resolve` 回调传递组件定义
resolve({
template: 'I am async!'
})
}, 1000)
})
如你所见,这个工厂函数会收到一个 resolve
回调,这个回调函数会在你从服务器得到组件定义的时候被调用。你也可以调用 reject(reason)
来表示加载失败。这里的 setTimeout
是为了演示用的,如何获取组件取决于你自己。一个推荐的做法是将异步组件和 webpack 的 code-splitting 功能一起配合使用:
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 `require` 语法将会告诉 webpack
// 自动将你的构建代码切割成多个包,这些包
// 会通过 Ajax 请求加载
require(['./my-async-component'], resolve)
})
你也可以在工厂函数中返回一个 Promise
Vue.component(
'async-webpack-example',
// 这个动态导入会返回一个 `Promise` 对象。
() => import('./my-async-component')
)
当使用局部注册的时候,你也可以直接提供一个返回 Promise
的函数
components: {
'my-component': () => import('./my-async-component')
}
处理加载状态(Vue2.3.0+版本新增)
异步组件工厂函数也可以返回一个如下格式的对象:
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
注意如果你希望在
Vue Router的路由组件中使用上述语法的话,你必须使用 Vue Router 2.4.0+ 版本。
字符串模板来源:
- 单文件组件。以.vue后缀结尾的文件。
- 内联模板字符串,例如template:'...'。
DOM 模板来源:以.html后缀结尾的文件。
如果你想在 DOM 中直接书写 Vue 模板,Vue 则必须从 DOM 中获取模板字符串。因为浏览器的原生 HTML 解析行为,因此有一些需要注意的事项
大小写区分
HTML 标签和属性名称是不分大小写的,所以浏览器会把任何大写的字符解释为小写。这意味着当你使用 DOM 内的模板时,无论是PascalCase形式的组件名称、camelCase形式的prop名称还是v-on的事件名称,都需要转换为相应等价的kebab-case(短横线连字符)形式
闭合标签
我们在上面的例子中已经使用过了闭合标签(self-closing tag)
hello
将被解析为:
hello
元素位置限制
某些 HTML 元素对于放在其中的元素类型有限制,例如