参考:
https://cn.vuejs.org/guide/introduction.html
https://blog.csdn.net/weixin_42371679/article/details/112408800
vue是一个js库,它基于标准html、css和js,并提供了一套声明式的、组件化的编程模型,可高效的开发用户界面。
简单应用举例
我们只需引入vue.min.js就可以使用vue。在这个例子中,我们引入vue.min.js,并使用vue来操作dom
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<!-- 当我们引入这个js之后,浏览器内存中就多了一个Vue构造函数 -->
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<p>{{ message }}</p> //使用{{}}语法,Vue实例中data下面的属性可以直接引用
</div>
<script>
//创建一个Vue实例
new Vue({
el: '#app',//挂载点 data: {//data属性中存放el中要用到的数据 message: 'Hello Vue.js!' //通过Vue提供的指令渲染message到页面 } }) </script> </body> </html>
这里,我们创建了一个vue实例用于挂载这个dom。运行结果如下
1 一些概念
1.1 单文件组件SFC
在vue中,我们可以创建一个.vue文件来创建一个组件,称为单文件组件。它的格式类似html格式,并且把js、html、css封装在同一个文件里,如下是一个例子
demo.vue
<script>
export default {
data() {
return {
count: 0 } } } </script> <template> <button @click="count++">Count is: {{ count }}</button> </template> <style scoped> button { font-weight: bold; } </style>
需要注意的是,我们必须使用构建工具(webpack或者vue-cli-service)来对.vue文件进行处理,也就是说我们编写的.vue组件,经过webpack进行构建之后,会生成原生的js代码,才能够被浏览器执行。浏览器默认不认识.vue方式定义的组件。当然,我们可以不使用.vue和webpack来定义组件,方式是使用Vue.component()方法,详情见16.1.2小节
1.2 选项式API和组合式API
vue组件可以按照两种不同的风格来写:选项式 API 和组合式 API
选项式API是一个对象,该对象包含了data、methods、mounted等属性,这些属性都会暴露在函数内部的this上,这个this指向当前组件实例。
<script>
export default {
// data() 返回的属性将会成为响应式的状态
// 并且暴露在 `this` 上
data() {
return {
count: 0 } }, // methods 是一些用来更改状态与触发更新的函数 // 它们可以在模板中作为事件监听器绑定 methods: { increment() { this.count++ } }, // 生命周期钩子会在组件生命周期的各个不同阶段被调用 // 例如这个函数就会在组件挂载完成后被调用 mounted() { console.log(`The initial count is ${this.count}.`) } } </script> <template> <button @click="increment">Count is: {{ count }}</button> </template>
组合式API
<script setup>
import { ref, onMounted } from 'vue'
// 响应式状态
const count = ref(0) // 用来修改状态、触发更新的函数 function increment() { count.value++ } // 生命周期钩子 onMounted(() => { console.log(`The initial count is ${count.value}.`) }) </script> <template> <button @click="increment">Count is: {{ count }}</button> </template>
2 使用vue脚手架创建vue项目
D:\2022\front\vue>npm init vue@latest
Need to install the following packages:
create-vue@latest
Ok to proceed? (y) y
Vue.js - The Progressive JavaScript Framework
√ Project name: ... vue-project √ Add TypeScript? ... No / Yes √ Add JSX Support? ... No / Yes √ Add Vue Router for Single Page Application development? ... No / Yes √ Add Pinia for state management? ... No / Yes √ Add Vitest for Unit Testing? ... No / Yes √ Add Cypress for both Unit and End-to-End testing? ... No / Yes √ Add ESLint for code quality? ... No / Yes Scaffolding project in D:\2022\front\vue\vue-project... Done. Now run: cd vue-project npm install npm run dev D:\2022\front\vue>
安装依赖并启动项目
> cd vue-project
> npm install
> npm run dev
3 html中直接使用ES模块导入和使用vue
现在绝大多数浏览器都支持ES,下面的例子,我们使用了<script type="module">,并且导入vue
<div id="app">{{ message }}</div>
<script type="module">
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js' createApp({ data() { return { message: 'Hello Vue!' } } }).mount('#app') </script>
这个例子,我们不需要使用构建工具(比如webpack)构建,直接在浏览器中打开即可
4 import maps
上面例子中,我们使用url导入vue,其实我们可以指定vue的多个源,通过<script type="importmap">设定
<script type="importmap">
{
"imports": { "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js" } } </script> <div id="app">{{ message }}</div> <script type="module"> import { createApp } from 'vue' createApp({ data() { return { message: 'Hello Vue!' } } }).mount('#app') </script>
5 根组件
每个应用都需要一个根组件,其他组件作为子组件
import { createApp } from 'vue'
// 从一个单文件组件中导入根组件
import App from './App.vue'
const app = createApp(App)
比如上例,根组件是App.vue,然后我们将这个组件作为createApp()的参数,createApp()作用是创建应用实例。
6 挂载
模板
<div id="app"></div>
js
app.mount('#app')
我们应该这么理解,我们创建了一个应用实例app,然后我们有一个html中的挂载点,我们将这个应用实例挂载到html的挂载点中。其中这个挂载点称为挂载容器。
当在未采用构建流程中使用vue时,可以在挂载容器中直接书写根组件模板
容器
<div id="app">
<button @click="count++">{{ count }}</button>
</div>
js
import { createApp } from 'vue'
const app = createApp({
data() {
return {
count: 0
}
}
})
app.mount('#app')
当根组件没有template选项时,Vue 将自动使用容器的 innerHTML 作为模板
7 模板语法
vue使用一种基于html的模板语法,使我们能够声明式的将vue组件实例的数据绑定到dom树上。而且所有的vue模板可以看做合法的html文档,即使没有vue引擎,浏览器也能够正常解析它,只不过其中的{{}}绑定的数据不能够被替换罢了。
在底层,vue会将模板编译为优化了的js代码。结合响应式系统,当vue应用状态发生变更时,vue将会重新渲染这些dom。
7.1 模板中的数据绑定形式
文本插值
<span>Message: {{ msg }}</span>
文本插值是最基本的数据绑定形式,其语法为{{}},双大括号标签会被替换为组件实例中 msg 属性的值。同时每次模板中 msg 属性更改时它也会同步更新
文本插值和使用v-text能够达到相同的效果,但是如果有字符串拼接的话,v-text指令不满足要求,需要使用文本插值。此外,v-text没有闪烁问题,文本插值在网速慢的情况下会有闪烁问题。
原始html
如果我们想向模板中插入原始html标签(比如<span style="color: red">This should be red.</span>),需要使用v-html属性
<p>Using text interpolation: {{ rawHtml }}</p>//错误
<p>Using v-html directive: <span v-html="rawHtml"></span></p>//正确
这里我们做的事情简单来说就是:在当前组件实例上,将此元素的 innerHTML 与 vue组件实例的rawHtml 属性保持同步。span 的内容将会被替换为 rawHtml 属性的值,插值为纯 HTML——数据绑定将会被忽略
属性绑定
如果需要绑定模板中html标签的属性,需要如下操作
<div v-bind:id="dynamicId"></div>
上例中,将div元素的id属性和vue组件实例的dynamicId属性绑定在一起,如果vue组件实例的dynamicId是null或undefined,那么div元素的id属性将会被移除
vue中,将dynamicId看做一个js表达式执行,所以我们还可以这样写<div v-bind:id="dynamicId+'123'"></div>,来做一些灵活的变化。
因为v-bind非常常用,所以简写形式如下
<div :id="dynamicId"></div>
如果vue组件实例中的属性包含多个值,例如如下
data() {
return {
objectOfAttrs: {
id: 'container',
class: 'wrapper'
}
}
}
通过不带参数的 v-bind,你可以将它们绑定到模板的单个元素上
<div v-bind="objectOfAttrs"></div>
双向属性绑定
v-model是唯一一个可以进行双向属性绑定的指令。注意:v-model只能应用到表单元素中
使用js表达式
表达式都会以组件为作用域解析执行。
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>
在vue中javascript表达式可以被用于如下场景:
在文本插值中放到{{}}内部
在vue指令(v-开头的特殊属性)的属性值中
7.2 vue指令
vue指令是以v-开头的特殊属性。
指令的值必须是一个javascript表达式(除少数几个例外)。
一个指令的作用是在其表达式的值发生变化时,响应式的更新dom。
<p v-if="seen">Now you see me</p>
这里v-if指令会基于seen表达式的值来移除/插入该<p>元素。
指令参数
<a v-on:click="doSomething"> ... </a>
<!-- 简写 -->
<a @click="doSomething"> ... </a>
上例中,指令v-on的参数是click,参数值是doSomething。
动态参数
<a v-bind:[attributeName]="url"> ... </a>
<!-- 简写 -->
<a :[attributeName]="url"> ... </a>
在指令的参数上可以使用一个javascript表达式,这个表达式放到[]中,称为动态参数。上例中attributeName会作为一个javascript表达式被动态的执行,计算得到的值会被用作最终的参数。举例来说,如果attribteName表达式的值是href,则这个绑定就等价于v-bind:href
相似的还可以将一个函数绑定到动态的事件名称上
<a v-on:[eventName]="doSomething"> ... </a>
<!-- 简写 -->
<a @[eventName]="doSomething">
如果eventName表达式的值是focus,就等价于v-on:focus
修饰符
这里的修饰符指的是指令参数的修饰符
<form @submit.prevent="onSubmit">...</form>
上例中,.prevent就是修饰符,这个修饰符会告知v-on指令对触发的事件调用e.preventDefault()
8 响应式
8.1 状态
我们以选项式API为例
用 data 选项来声明组件的响应式状态。此选项的值应为返回一个对象的函数。Vue 将在创建新组件实例的时候调用此函数,并将函数返回的对象用响应式系统进行包装。此对象的所有顶层属性都会被代理到组件实例 (即方法和生命周期钩子中的 this) 上。(我们可以在浏览器console窗口执行window.vue查看,看看我们定义的vue实例下是否有data中的属性)
export default {
data() {
return {
count: 1
}
},
// `mounted` 是生命周期钩子,之后我们会讲到
mounted() {
// `this` 指向当前组件实例
console.log(this.count) // => 1
// 数据属性也可以被更改
this.count = 2 } }
组件实例上的属性仅在实例首次创建时被添加,因此你需要确保它们都出现在 data 函数返回的对象上,如果值暂时未准备好,必要时可以使用null、undefined等占位。
8.2 方法
要为组件添加方法,我们需要用到 methods 选项。它应该是一个包含所有方法的对象
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
},
mounted() {
// 在其他方法或是生命周期中也可以调用方法
this.increment() } }
vue自动为methods中的方法绑定了永远指向组件实例的this。需要注意的是,methods中的方法不能使用箭头函数,因为箭头函数没有自己的this上下文
export default {
methods: {
increment: () => {
// 反例:无法访问此处的 `this`!
}
}
}
模板上访问方法
<button @click="increment">{{ count }}</button>
8.3 DOM更新时机
当我们修改了响应式状态之后,dom会自动更新,但是,vue更新dom有自己的节奏,按照固定的时间周期更新。如果我们想访问更新后的dom,需要使用如下方式
import { nextTick } from 'vue'
export default {
methods: {
increment() {
this.count++
nextTick(() => {
// 访问更新后的 DOM
}) } } }
8.4 computed
export default {
data() {
return {
author: {
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide', 'Vue 4 - The Mystery' ] } } }, computed: { // 一个计算属性的 getter publishedBooksMessage() { // `this` 指向当前组件实例 return this.author.books.length > 0 ? 'Yes' : 'No' } } }
模板中的应用
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
在模板中使用计算属性的方式和使用一般属性的方式并无二致。Vue 会检测到 this.publishedBooksMessage 依赖于 this.author.books,所以当 this.author.books 改变时,任何依赖于 this.publishedBooksMessage 的绑定都将同时更新
computed属性缓存和方法比较
上例中,我们如果定义一个方法,同样可以达到一样的效果
// 组件中
methods: {
calculateBooksMessage() {
return this.author.books.length > 0 ? 'Yes' : 'No'
}
}
模板中调用方法
<p>{{ calculateBooksMessage() }}</p>
但是methods和computed是有区别的。computed属性是当状态属性发生变化时,触发computed属性的计算,然后缓存,如果状态属性没有变化,则会一直使用这个缓存。而methods会每次都会被调用
可写computed属性
computed属性默认是只读的。我们可以通过提供setter来使其可写。
export default {
data() {
return {
firstName: 'John',
lastName: 'Doe'
}
},
computed: {
fullName: {
// getter
get() { return this.firstName + ' ' + this.lastName }, // setter set(newValue) { // 注意:我们这里使用的是解构赋值语法 [this.firstName, this.lastName] = newValue.split(' ') } } } }
现在当你再运行 this.fullName = 'John Doe' 时,setter 会被调用而 this.firstName 和 this.lastName 会随之更新
8.5 绑定class
<div :class="{ active: isActive }"></div>
如果isActive=true的话,渲染结果是
<div :class=‘active’></div>
我们也可以在一个class上绑定多个值
data() {
return {
isActive: true,
hasError: false
}
}
配合如下模板
<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"
></div>
渲染结果为
<div class="static active"></div>
另外,一种更简便的方式是我们可以直接绑定一个对象
data() {
return {
classObject: {
active: true,
'text-danger': false
}
}
}
模板
<div :class="classObject"></div>
绑定数组
data() {
return {
activeClass: 'active',
errorClass: 'text-danger'
}
}
模板
<div :class="[activeClass, errorClass]"></div>
渲染结果
<div class="active text-danger"></div>
8.6 绑定内联样式
:style 支持绑定 JavaScript 对象值,对应的是 HTML 元素的 style 属性
data() {
return {
activeColor: 'red',
fontSize: 30
}
}
对应的模板
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
8.7 条件渲染v-if
v-if指令用于条件性的渲染一块内容。
<h1 v-if="awesome">Vue is awesome!</h1>
另外还有v-else和v-else-if
<button @click="awesome = !awesome">Toggle</button>
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
一个 v-else 元素必须跟在一个 v-if 或者 v-else-if 元素后面,否则它将不会被识别
<template> 上的 v-if
如果我们想要切换不止一个元素,在这种情况下我们可以在一个 <template> 元素上使用 v-if,这只是一个不可见的包装器元素,最后渲染的结果并不会包含这个 <template> 元素
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
8.8 v-show
v-show和v-if效果差不多,都是根据一个布尔值决定元素的显式与否,但是他们是有区别的,v-if为false时,会移除dom元素,v-show为false时不会移除dom元素,只是隐藏该dom元素
v-if有较高的性能消耗,v-show有较高的初始性能消耗。如果元素频繁的进行显式和隐藏的切换,建议使用v-show。
8.8 v-for
data() {
return {
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
模板中的使用
<li v-for="item in items">
{{ item.message }}
</li>
v-for 也支持使用可选的第二个参数表示当前项的位置索引
<li v-for="(item, index) in items">
{{ index }} - {{ item.message }}
</li>
可以在定义 v-for 的变量别名时使用解构
<li v-for="{ message } in items">
{{ message }}
</li>
<!-- 有 index 索引时 -->
<li v-for="({ message }, index) in items">
{{ message }} {{ index }}
</li>
也可以这样使用:item of items
<div v-for="item of items"></div>
也可以使用 v-for 来遍历一个对象的所有属性。遍历的顺序会基于对该对象调用 Object.keys() 的返回值来决定
data() {
return {
myObject: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10' } } }
模板
<ul>
<li v-for="value in myObject">
{{ value }}
</li>
</ul>
可以通过提供第二个参数表示属性名 (例如 key)
<li v-for="(value, key) in myObject">
{{ key }}: {{ value }}
</li>
第三个参数表示位置索引
<li v-for="(value, key, index) in myObject">
{{ index }}. {{ key }}: {{ value }}
</li>
可以在v-for中使用范围值
<span v-for="n in 10">{{ n }}</span>
在该例中,v-for 直接接受一个整数值。Vue会将该模板基于1…10循环10次。
在<template> 上使用 v-for
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
通过key管理状态
Vue 默认按照“就地更新”的策略来更新通过 v-for 渲染的元素列表。当数据项的顺序改变时,Vue 不会随之移动 DOM 元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染
默认模式是高效的,但只适用于列表渲染输出的结果不依赖子组件状态或者临时 DOM 状态 (例如表单输入值) 的情况
有时候,我们希望dom顺序进行重排,而不是按照默认顺序进行渲染。此时我们可以为每个元素对应的块提供一个唯一的 key
<div v-for="item in items" :key="item.id">
<!-- 内容 -->
</div>
当你使用 <template v-for> 时,key 应该被放置在这个 <template> 容器上
<template v-for="todo in todos" :key="todo.name">
<li>{{ todo.name }}</li>
</template>
9 事件处理
使用 v-on 指令 (简写为 @) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript。用法:v-on:click="methodName" 或 @click="handler"
本质上,methodName或者handler是一个js表达式,而js表达式是要有返回值的,所以,我们可以使用内联事件处理。
9.1 内联事件处理
data() {
return {
count: 0
}
}
模板
<button @click="count++">Add 1</button>
<p>Count is: {{ count }}</p>
9.2 方法事件处理
data() {
return {
name: 'Vue.js'
}
},
methods: {
greet(event) {
// 方法中的 `this` 指向当前活跃的组件实例
alert(`Hello ${this.name}!`)
// `event` 是 DOM 原生事件
if (event) { alert(event.target.tagName) } } }
模板
<!-- `greet` 是上面定义过的方法名 -->
<button @click="greet">Greet</button>
9.3 在内联事件处理中访问事件参数
有时我们需要在内联事件处理器中访问原生 DOM 事件。你可以向该处理器方法传入一个特殊的 $event 变量,或者使用内联箭头函数
<!-- 使用特殊的 $event 变量 -->
<button @click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
<!-- 使用内联箭头函数 -->
<button @click="(event) => warn('Form cannot be submitted yet.', event)">
Submit
</button>
9.4 事件修饰符
.stop
.stop这个修饰符可以阻止事件的向上冒泡传递。
<div @click="divHandler">
<input type="button" value="按钮" @click.stop="btnHandler" />
</div>
这个例子,点击事件将只作用于按钮,不会向上冒泡传递到div。
.prevent
这个修饰符作用是阻止默认事件
<a href="www.baidu.com" @click.prevent="linkClick">百度一下</a>
浏览器赋予了a链接的默认行为是跳转到href指定的链接,有时我们不希望浏览器默认行为,可以使用.prevent修饰符
.capture
这个修饰符作用是实现捕获触发事件机制。该机制和事件冒泡机制是反方向的。
<div @click.capture="divHandler">
<input type="button" value="按钮" @click="btnHandler" />
</div>
这个例子中,外层的div将会首先接收到事件,然后button才接收到事件。
.self
这个修饰符作用是,屏蔽掉冒泡事件,只有点击自身才会触发事件
<div @click.self="divHandler">
<input type="button" value="按钮" @click="btnHandler" />
</div>
这个例子中,当我们点击按钮,事件将不会传递到div。只有我们点击div时事件才会触发。
.once
事件只会触发一次
10 表单输入绑定
11 生命周期
vue组件实例是有生命周期的,组件实例从创建到销毁,会经历设置数据侦听、编译模板、挂载等过程。在数据改变的时候会更新dom。
vue为我们定义了很多生命周期钩子,让开发者能够在特定阶段运行自己的代码。
那么如何注册一个生命周期钩子呢?我们以mounted钩子为例说明
export default {
mounted() {
console.log(`the component is now mounted.`)
}
}
所有生命周期钩子函数的this都会自动指向当前vue组件实例。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<h3 id='h3'>{{ msg }}</h3>
</div>
<script>
new Vue({
el: '#app',
data: {
msg: 'ok'
},
methods: {
show() {
console.log('执行了show方法')
}
},
beforeCreate() {
console.log('beforeCreate', 'Vue实例创建之前的生命周期钩子')
console.log('beforeCreate', this.msg) // 打印undefined
console.log('beforeCreate',this.show) // 打印undefined
},
created() {
console.log('created','Vue实例创建之后的生命周期钩子')
console.log('created',this.msg)
console.log('created',this.show)
},
beforeMount() {
console.log('beforeMount','templete模板已经在内存中编辑完成,但是尚未把模板渲染到页面')
console.log('beforeMount',document.getElementById('h3').innerText)
},
mounted() {
console.log('mounted','将内存中编辑好的模板替换到浏览器的真实dom中去后,调用此钩子')
console.log('mounted',document.getElementById('h3').innerText)
},
beforeUpdate() {
},
updated() {
}
})
</script>
</body>
</html>
12 侦听器
在选项式 API 中,我们可以使用 watch 选项在每次响应式属性发生变化时触发一个函数
export default {
data() {
return {
question: '',
answer: 'Questions usually contain a question mark. ;-)'
}
},
watch: {
// 每当 question 改变时,这个函数就会执行
question(newQuestion, oldQuestion) { if (newQuestion.includes('?')) { this.getAnswer() } } }, methods: { async getAnswer() { this.answer = 'Thinking...' try { const res = await fetch('https://yesno.wtf/api') this.answer = (await res.json()).answer } catch (error) { this.answer = 'Error! Could not reach the API. ' + error } } } }
模板
<p>
Ask a yes/no question:
<input v-model="question" />
</p>
<p>{{ answer }}</p>
即时回调的侦听器
export default {
// ...
watch: {
question: {
handler(newQuestion) {
// 在组件实例创建时会立即调用
},
// 强制立即执行回调
immediate: true
}
}
// ...
}
回调的触发时机
当你更改了响应式状态,它可能会同时触发 Vue 组件更新和侦听器回调。
默认情况下,用户创建的侦听器回调,都会在 Vue 组件更新之前被调用。这意味着你在侦听器回调中访问的 DOM 将是被 Vue 更新之前的状态。
如果想在侦听器回调中能访问被 Vue 更新之后的DOM,你需要指明 flush: 'post' 选项
export default {
// ...
watch: {
key: {
handler() {},
flush: 'post'
}
}
}
this.$watch()
我们也可以使用组件实例的 $watch() 方法来命令式地创建一个侦听器
export default {
created() {
this.$watch('question', (newQuestion) => {
// ...
})
}
}
13 过滤器filter
13.1 全局过滤器
定义
Vue.filter('过滤器的名字', function(data, ){}) //data是过滤器管道符前面的
过滤器的调用方式
{{ name | 过滤器的名字 }}
例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<p>{{ msg | msgFormat('邪恶','疯狂') | secondFormat }}</p>
</div>
<script>
Vue.filter('msgFormat', function(msg, arg1, arg2){
return msg.replace(/单纯/g, arg1+arg2)
})
Vue.filter('secondFormat', function(msg){
return msg+'========'
})
new Vue({
el: '#app',
data: {
msg: '曾经,我也是一个单纯的少年,单纯的我,傻傻的问,谁是世界上最单纯的男人'
}
})
</script>
</body>
</html>
如上例所示,过滤器可以通过管道符进行多次使用
13.2 私有过滤器
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<p>{{ msg | msgFormat('邪恶','疯狂') }}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
msg: '曾经,我也是一个单纯的少年,单纯的我,傻傻的问,谁是世界上最单纯的男人'
},
filters: {
msgFormat: function(msg, arg1, arg2) {
return msg.replace(/单纯/g, arg1+arg2)
}
}
})
</script>
</body>
</html>
14 自定义指令
14.1 自定义全局指令Vue.directive
Vue中所有的指令在调用时都以v-开头,自定义指令也要遵守这个规则
自定义指令的语句是:Vue.directive('指令名称', {xxx})
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<input v-focus></input>
</div>
<script>
Vue.directive('focus', {
bind:function(el) {//每当指令绑定到元素上时会执行,只执行1次
},
inserted:function(el) {//元素插入到DOM中时会执行,只执行1次,其中el是原生dom对象
el.focus()
},
updated:function(el) {//当VNode更新的时候会执行,可能会触发多次
}
})
new Vue({
el: '#app',
data: {
msg: '曾经,我也是一个单纯的少年,单纯的我,傻傻的问,谁是世界上最单纯的男人'
},
filters: {
msgFormat: function(msg, arg1, arg2) {
return msg.replace(/单纯/g, arg1+arg2)
}
}
})
</script>
</body>
</html>
自定义指令有一些钩子函数,我们还可以为这些钩子函数提供参数,可参考官网文档https://cn.vuejs.org/guide/reusability/custom-directives.html。
14.2 自定义私有指令directives
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<h3 v-fontweight="100">fdfd</h3>
</div>
<script>
new Vue({
el: '#app',
data: {
},
directives: {
'fontweight': {
bind: function(el, binding) {
el.style.fontWeight = binding.value
}
}
}
})
</script>
</body>
</html>
简写形式
对于自定义指令来说,一个很常见的情况是仅仅需要在 mounted 和 updated 上实现相同的行为,除此之外并不需要其他钩子。这种情况下我们可以直接用一个函数来定义指令。
<script>
new Vue({
el: '#app',
data: {
},
directives: {
'fontweight': { // 普通形式
bind: function(el, binding) {
el.style.fontWeight = binding.value
}
},
'fontsize': function(el, binding) { //简写形式
el.style.fontSize = binding.value
}
}
})
15 http请求
15.1 使用vue-resource发送请求
15.2 使用axios发送请求
16 创建组件
16.1 创建全局组件
16.1.1 使用Vue.extend()创建组件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<my-com1></my-com1>
</div>
<script>
var com1 = Vue.extend({
template: '<h3>这是使用Vue.extend创建的组件</h3>'
})
Vue.component('myCom1', com1)
var vm = new Vue({
el: '#app'
});
</script>
</body>
</html>
其中,我们可以简写为如下形式
Vue.component('myCom1', Vue.extend({
template: '<h3>这是使用Vue.extend创建的组件</h3>'
}))
16.1.2 使用Vue.component创建组件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<my-com1></my-com1>
</div>
<script>
Vue.component('myCom1', {
template: '<h3>这是直接使用Vue.component创建的组件</h3>'
})
var vm = new Vue({
el: '#app'
});
</script>
</body>
</html>
16.1.3 使用<template>元素创建组件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<my-com1></my-com1>
</div>
<template id='tmp1'>
<h3>这是通过template元素在外部定义的组件结构</h3>
</template>
<script>
Vue.component('myCom1', {
template: '#tmp1'
})
var vm = new Vue({
el: '#app'
});
</script>
</body>
</html>
16.2 创建私有组件
目前我们已经知道了组件所包含的一些属性,如下
<body>
<div id="app">
</div>
<script>
var vm = new Vue({
el: '#app',
data: {},
methods: {},
filters: {},
directives: {},
beforeCreate(){},
created(){},
//其他一些声明周期钩子函数省略
});
</script>
</body>
现在我们再认识一个属性,用于定义私有组件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<login/>
</div>
<script>
var vm = new Vue({
el: '#app',
components: {
login: {
template: '<h1>这是私有的login组件</h1>'
}
}
});
</script>
</body>
</html>
其中,template可以单独提取出来
<template id='tmpl'>
<h1>这是私有的login组件</h1>
</template>
<script>
var vm = new Vue({
el: '#app',
components: {
login: {
template: '#tmpl'
}
}
});
</script>
我们还可以这样写
<html>
<head>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<login></login>
</div>
<script>
var login = {
template: '<h3>这是登陆组件</h3>'
}
var vm = new Vue({
el: '#app',
components: {
login
}
})
</script>
</body>
</html>
其中 components: { login } 相当于 components: { login:login }
17 组件中的data
组件可以有自己的data,但是组件中的data和实例化Vue根组件时的data写法有点不一样,组件中的data必须是一个函数,且这个函数返回一个对象
组件中的data和Vue根组件中的data使用方法一样,可以在<template>中通过{{}}引用
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<my-com/>
</div>
<script>
Vue.component('myCom',{
template: '<h1>这是全局组件-----{{ msg }}</h1>',
data: function() {
return {
msg: '这是组件中的data定义的数据'
}
}
})
var vm = new Vue({
el: '#app'
});
</script>
</body>
</html>
注意组件中的data必须返回一个独立的对象,如果是共享的对象,则会出现问题
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<counter></counter>
<hr>
<counter></counter>
</div>
<template id='tmpl'>
<div>
<input type='button' value='+1' @click='increment'>
<h3>{{count}}</h3>
</div>
</template>
<script>
var obj = {count:0}
Vue.component('counter',{
template: '#tmpl',
data: function() {
return obj
},
methods: {
increment() {
this.count++
}
}
})
var vm = new Vue({
el: '#app'
});
</script>
</body>
</html>
如下图所示,当我们点击一个组件时,另一个组件的count也在增加
所以,正确的做法是:
Vue.component('counter',{
template: '#tmpl',
data: function() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
})
18 组件切换
这里我们用到一个vue中的新组件<component>来承载我们自定义组件。我们定义了2个a标签,点击标签通过改变data来改变<component>的:is属性绑定的值,从而改变要显示的自定义组件
/<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<a href="" @click.prevent="comName='login'">登陆</a>
<a href="" @click.prevent="comName='register'">注册</a>
<component :is="comName"></component>
</div>
<script>
Vue.component('login', {
template: '<h3>登陆组件</h3>'
})
Vue.component('register', {
template: '<h3>注册组件</h3>'
})
new Vue({
el: '#app',
data: {
comName: 'login'
}
})
</script>
</body>
</html>
19 组件传值
19.1 传递属性
父组件可以在引用子组件的时候,通过属性绑定(v-bind)的形式把需要传递给子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用.
在子组件中需要定义props属性来接收父组件传递过来的值。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<com1 v-bind:parentmsg="msg"></com1>
</div>
<script>
new Vue({
el: '#app',
data: {
msg: '123 啊-父组件中的数据'
},
components: {
com1: {
template: '<h1>这是子组件 --- {{ parentmsg }}</h1>',
props: ['parentmsg']
}
}
})
</script>
</body>
</html>
19.2 传递方法
除了把父组件data中的值传递到子组件,还可以把父组件中的方法传递到子组件。
父组件向子组件传递方法,使用的是事件绑定机制v-on
当我们自定义了一个事件属性之后,子组件就能够通过某些方式,来调用传递进去这个方法了
当点击子组件的按钮时,如何拿到父组件传递过来的func方法并调用呢?使用this.$emit('func')的方式
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<com1 v-on:func="show"></com1>
</div>
<template id="tmp1">
<div>
<h1>这是子组件</h1>
<input type="button" value="这是子组件中的按钮 - 点击它,触发父组件传递过来的func方法" @click="myclick"></input>
</div>
</template>
<script>
var com1 = {
template: '#tmp1',
methods: {
myclick() {
this.$emit('func','abc')
}
}
}
new Vue({
el: '#app',
data: {},
methods: {
show(param) {
console.log('调用了父组件的show方法'+param)
}
},
components: {
com1
}
})
</script>
</body>
</html>
上面例子中,我们在子组件中调用了父组件的方法,并且携带方法参数,通过这种方式,我们可以实现子组件向父组件传值。
20 this.$refs获取组件引用
我们在编写组件标签时,可以使用ref定义组件实例的引用。然后我们可以在其他组件中通过this.$refs获取组件的引用,进而可以访问组件中的data和调用组件的方法
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<input value="获取元素" type="button" @click.prevent="getElement">
<login ref="mylogin"></login>
</div>
<script>
var login = {
template: '<h1>登陆组件</h1>',
data() {
return {
msg: 'son msg'
}
},
methods: {
show() {
console.log('调用了子组件的方法')
}
}
}
var vm = new Vue({
el: '#app',
data: {},
methods: {
getElement() {
console.log(this.$refs.mylogin.msg)
this.$refs.mylogin.show()
}
},
components: {
login
}
})
</script>
</body>
</html>
21 vue-router路由
路由是干啥的?路由是进行不同组件切换的。
我们可以直接在<script>标签中从cdn引入vue-router;也可以使用npm安装vue-router包
如果在一个模块化工程中使用vue-router,必须使用Vue.use明确的安装路由功能
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
21.1 基本使用
当我们页面中引入了 <script src="https://unpkg.com/vue-router@3.0.1/dist/vue-router.js"></script> ,在window的全局对象上就有了一个路由的构造函数。叫VueRouter
下面我们通过一个实例来展示其基本用法
1 首先,创建VueRouter对象 routerObj ,创建时,我们传递一个对象,这个对象里面包含路由匹配规则
每个路由匹配规则是一个对象,这个对象上有两个必须的属性:path:表示监听哪个路由链接地址;component:表示如果路由匹配成功需要展示的组件
2 然后,通过在Vue实例中使用 router: routerObj 将路由规则对象注册到Vue实例上,用来监听URL地址变化,然后展示对应的组件
3 使用 <router-view> 标签作为一个占位符,用来替换路由匹配到的组件。 <router-view> 是vue-router提供的元素,专门用来当做占位符的,将来路由规则匹配到的组件,就会展示到这个router-view中去
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script src="https://unpkg.com/vue-router@3.0.1/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<a href="#/login">登陆</a>
<a href="#/register">注册</a>
<!-- 这是vue-router提供的元素,专门用来当做占位符的,将来路由规则匹配到的组件,就会展示到这个router-view中去 -->
<router-view></router-view>
</div>
<script>
var login = {
template: '<h1>登陆组件</h1>'
}
var register = {
template: '<h1>注册组件</h1>'
}
var routerObj = new VueRouter({
routes: [ //路由匹配规则
//每个路由匹配规则是一个对象,这个对象上有两个必须的属性:path:表示监听哪个路由链接地址;component:表示如果路由匹配成功需要展示的组件
{path: '/login', component: login}, //注意:component的属性值必须是一个组件模板对象,不能是组件的引用名称
{path: '/register', component: register}
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router: routerObj //将路由规则对象注册到Vue实例上,用来监听URL地址变化,然后展示对应的组件
})
</script>
</body>
</html>
效果展示
执行过程:
1 点击登陆或者注册按钮,使得url地址发生改变
2 vue-router监听到url地址的变化,通过路由匹配规则,找到匹配到的组件
3 把匹配到的组件替换掉 <router-view> 占位符
21.2 router-link
上栗中,我们使用a标签来达到路由切换组件的目的,此外vue-router为我们提供了一个<router-link>标签达到相同的目的
<div id="app">
<router-link to="/login">登陆</router-link>
<router-link to="/register">注册</router-link>
<router-view></router-view>
</div>
router-link默认渲染为一个a标签,我们还可以使用tag属性设置渲染为其他标签
<router-link to="/login" tag="span">登陆</router-link>
渲染后的结果如下所示
<div id="app">
<span class="router-link-exact-active router-link-active">登陆</span>
<a href="#/register" class="">注册</a>
<h1>登陆组件</h1>
</div>
21.3 redirect重定向
上例中,我们可以设置路由为/时重定向到另一个路由
{path: '/', redirect: '/login'}
21.4 路由传参
21.4.1 方式1
我们可以在router-link中使用如下方式传递参数 <router-link to="/login?id=10&name=szj">登陆</router-link> 。然后在当前组件中使用Vue实例上的this.$route对象的query属性获取传递的参数。
<html>
<head>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script src="https://unpkg.com/vue-router@3.0.1/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<router-link to="/login?id=10&name=szj">登陆</router-link>
<router-link to="/register">注册</router-link>
<router-view></router-view>
</div>
<script>
var login = {
template: '<h1>登陆组件---{{ this.$route.query.id }}---{{ this.$route.query.name }}</h1>'
}
var register = {
template: '<h1>注册组件</h1>'
}
var routerObj = new VueRouter({
routes: [
{path: '/login', component: login},
{path: '/register', component: register}
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router: routerObj
})
</script>
</body>
</html>
21.4.2 方式2
我们可以在router-link中使用如下方式传递参数 <router-link to="/login/10/szj">登陆</router-link> ,并且使用路由匹配规则方式为 {path: '/login/:id/:name', component: login} ,然后在当前组件中使用Vue实例上的this.$route对象的params属性获取传递的参数。
<html>
<head>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script src="https://unpkg.com/vue-router@3.0.1/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<router-link to="/login/10/szj">登陆</router-link>
<router-link to="/register">注册</router-link>
<router-view></router-view>
</div>
<script>
var login = {
template: '<h1>登陆组件---{{ $route.params.id }}---{{ $route.params.name }}</h1>'
}
var register = {
template: '<h1>注册组件</h1>'
}
var routerObj = new VueRouter({
routes: [
{path: '/login/:id/:name', component: login},
{path: '/register', component: register},
{path: '/', redirect: '/login'}
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router: routerObj
})
</script>
</body>
</html>
21.5 子路由
路由匹配规则里面,我们使用children属性定义子路由
<html>
<head>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script src="https://unpkg.com/vue-router@3.0.1/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<router-link to="/account">账号</router-link>
<router-view></router-view>
</div>
<template id="tmp1">
<div>
<h1>账号</h1>
<router-link to="/account/login">登陆</router-link>
<router-link to="/account/register">注册</router-link>
<router-view></router-view>
</div>
</template>
<script>
var account = {
template: '#tmp1'
}
var login = {
template: '<h3>登陆</h3>'
}
var register = {
template: '<h3>注册</h3>'
}
var routerObj = new VueRouter({
routes: [
{
path: '/account',
component: account,
children: [
{path: 'login', component: login},
{path: 'register', component: register}
]
}
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router: routerObj
})
</script>
</body>
</html>
效果如下
21.6 使用命名视图实现经典布局
我们定义3个组件,分别是header、left、main。并且我们使用路由规则配置这三个组件的路由链接,实现经典的布局
我们定义3个<router-view>,并且我们为之设置name属性,达到分别放置header、left、main组件的目的
<html>
<head>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script src="https://unpkg.com/vue-router@3.0.1/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<router-view></router-view>
<router-view name="left"></router-view>
<router-view name="main"></router-view>
</div>
<script>
var header = {
template: '<h3>Header头部区域</h3>'
}
var leftBox = {
template: '<h3>Left侧边栏区域</h3>'
}
var mainBox = {
template: '<h3>Main主体区域</h3>'
}
var routerObj = new VueRouter({
routes: [
{path: '/', components: {
'default': header,
'left': leftBox,
'main': mainBox
}}
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router: routerObj
})
</script>
</body>
</html>
结果如下所示
基本已经实现经典布局,缺的是样式,我们加上样式后的代码
<html>
<head>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script src="https://unpkg.com/vue-router@3.0.1/dist/vue-router.js"></script>
<style>
.html, body {
margin: 0;
padding: 0;
}
.header {
background: orange;
height: 80px;
}
.h3 {
margin: 0;
padding: 0;
font-size: 16px;
}
.container {
display: flex;
height: 600px;
}
.left {
background: lightgreen;
flex: 2;
}
.main {
background: lightpink;
flex: 8;
}
</style>
</head>
<body>
<div id="app">
<router-view></router-view>
<div class="container">
<router-view name="left"></router-view>
<router-view name="main"></router-view>
</div>
</div>
<script>
var header = {
template: '<h3 class="header">Header头部区域</h3>'
}
var leftBox = {
template: '<h3 class="left">Left侧边栏区域</h3>'
}
var mainBox = {
template: '<h3 class="main">Main主体区域</h3>'
}
var routerObj = new VueRouter({
routes: [
{path: '/', components: {
'default': header,
'left': leftBox,
'main': mainBox
}}
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router: routerObj
})
</script>
</body>
</html>
效果如下所示
22 使用Vue实例的render方法渲染组件
<html>
<head>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
</div>
<script>
var login = {
template: '<h3>这是登陆组件</h3>'
}
var vm = new Vue({
el: '#app',
data: {},
methods: {},
render: function(createElements) { //createElement是一个方法,调用它能够把指定的组件模板渲染为html结构
var html = createElements(login)
return html //这里的return结果,会替换页面中el指定的容器
}
})
</script>
</body>
</html>
createElement是一个方法,调用它能够把指定的组件模板渲染为html结构
这里的return结果,会替换页面中el指定的容器
23 vue cli
官网:https://cli.vuejs.org/zh/guide/
vue cli是进行vue开发的工具,当我们安装完vue cli后,就可以使用里面的一些命令方便的进行一些创建和管理方面的操作。比如使用vue create命令进行新项目的创建;vue-cli-service build进行项目的构建等等。也可以参考官网进行理解
vue cli包含了如下3部分
- @vue/cli
- @vue/cli-service
- @vue/cli-plugin-xxx或vue-cli-plugin-xxx
23.1 安装
我们使用如下命令查看已安装的全局包
npm ls --global --depth 0
如果没有安装,则安装
npm install -g @vue/cli --registry=https://registry.npm.taobao.org
验证是否安装成功
D:\2022\front\pms-front>vue --version
@vue/cli 5.0.8
23.2 创建一个项目
vue create hello-world
23.3 vue ui
这个命令将会打开浏览器,在浏览器页面中我们可以创建和管理项目
23.4 插件
如果你查阅一个新创建项目的 package.json,就会发现依赖都是以 @vue/cli-plugin- 开头的。插件可以修改 webpack 的内部配置,也可以向 vue-cli-service 注入命令。在项目创建的过程中,绝大部分列出的特性都是通过插件来实现的。
23.5 vue-cli-service
如果我们安装了@vue/cli-service,我们就可以使用vue-cli-service命令
D:\2022\front\vuecli\hello-world\node_modules\.bin>vue-cli-service --help
Usage: vue-cli-service <command> [options]
Commands:
serve start development server
build build for production
inspect inspect internal webpack config
run vue-cli-service help [command] for usage of a specific command.
使用命令 vue-cli-service serve 启动一个开发服务器,并附带开箱即用的模块热重载。
D:\2022\front\vuecli\hello-world\node_modules\.bin>vue-cli-service help serve
Usage: vue-cli-service serve [options] [entry]
Options:
--open open browser on server start
--copy copy url to clipboard on server start
--stdin close when stdin ends
--mode specify env mode (default: development)
--host specify host (default: 0.0.0.0)
--port specify port (default: 8080)
--https use https (default: false)
--public specify the public network URL for the HMR client
--skip-plugins comma-separated list of plugin names to skip for this run
其中命令行参数[entry]指定为唯一入口(默认值src/main.js)
使用 vue-cli-service build 用于构建用于生产环境的包
D:\2022\front\vuecli\hello-world\node_modules\.bin>vue-cli-service help build
Usage: vue-cli-service build [options] [entry|pattern]
Options:
--mode specify env mode (default: production)
--dest specify output directory (default: dist)
--no-module build app without generating <script type="module"> chunks for modern browsers
--target app | lib | wc | wc-async (default: app)
--inline-vue include the Vue module in the final bundle of library or web component target
--formats list of output formats for library builds (default: commonjs,umd,umd-min)
--name name for lib or web-component mode (default: "name" in package.json or entry filename)
--filename file name for output, only usable for 'lib' target (default: value of --name)
--no-clean do not remove the dist directory contents before building the project
--report generate report.html to help analyze bundle content
--report-json generate report.json to help analyze bundle content
--skip-plugins comma-separated list of plugin names to skip for this run
--watch watch for changes
--stdin close when stdin ends
23.6 环境变量
在vue cli创建的项目根路径下放置环境变量文件,如下
.env# 在所有的环境中被载入
.env.local # 在所有的环境中被载入,但会被 git 忽略
.env.[mode] # 只在指定的模式中被载入 .env.[mode].local # 只在指定的模式中被载入,但会被 git 忽略
比如,.env.production将用在生产环境中。
vue中采用第三方包dotenv(https://github.com/motdotla/dotenv#rules)实现环境变量的配置和加载。dotenv这个工具可以将.env中的配置加载到nodejs的全局环境变量process.env中。
但是,注入到process.env中的环境变量不一定就能够在vue项目中被使用。vue通过结合webpack的一个插件DefinePlugin(参考https://www.webpackjs.com/plugins/),可以实现只有VUE_APP开头的变量会被DefinePlugin静态的嵌入到代码中。
我们可以在代码中这样使用环境变量配置文件中的配置:
process.env.VUE_APP_SECRET
另外还有两个特殊的变量我们可以直接使用
NODE_ENV
- 会是"development"
、"production"
或"test"
中的一个。具体的值取决于应用运行的模式。BASE_URL
- 和vue.config.js
中的publicPath
对应,即你的应用会部署到的基础路径。
23.7 模式
默认情况下,一个vue cli项目有3种模式
development
模式用于vue-cli-service serve
test
模式用于vue-cli-service test:unit
production
模式用于vue-cli-service build
和vue-cli-service test:e2e
你可以通过传递--mode参数指定特定模式。例如,如果想在构建时使用开发环境变量:
vue-cli-service build --mode development
当运行vue-cli-service命令时,所有的环境变量都从对应的环境变量文件中载入。
NODE_ENV变量将决定web app运行的模式(开发、生产、测试)。这个变量可以在环境变量文件(.env)中配置,如果没有配置,会根据当前模式确定NODE_ENV的值。
23.8 html模板
public/index.html
文件是一个会被 html-webpack-plugin 处理的模板。在构建过程中,资源链接会被自动注入。
使用lodash template语法的html规则如下:
<%= BASE_URL %>
用来做不转义插值;<%- VALUE %>
用来做 HTML 转义插值;<% expression %>
用来描述 JavaScript 流程控制。
像BASE_URL、VUE_APP_XXX之类的环境变量都可以直接使用。
此外,html-webpack-plugin插件还提供了内置的一些变量供我们使用,参考:https://github.com/jantimon/html-webpack-plugin#writing-your-own-templates,示例
<html>
<head>
<%= htmlWebpackPlugin.tags.headTags %>
</head>
<body>
<%= htmlWebpackPlugin.tags.bodyTags %>
</body>
</html>
23.9 静态资源
静态资源可以通过两种方式进行处理:
-
在 JavaScript 被导入或在 template/CSS 中通过相对路径被引用。这类引用会被 webpack 处理。
-
放置在
public
目录下或通过绝对路径被引用。这类资源将会直接被拷贝,而不会经过 webpack 的处理。webpack打包之后这些资源将会出现在打包路径的根路径下。
从相对路径导入的静态资源,该资源将会被包含到webpack的依赖视图中,在其编译的过程中,所有诸如<img src="...">
、background: url(...)
的资源将会被解析为一个模块依赖。例如
url(./image.png) 会被翻译为 require('./image.png')
<img src="./image.png"> 会被编译为一个hash值 h('img', { attrs: { src: require('./image.png') }}) 。在其内部,我们通过 webpack 的 Assets Modules 配置,用版本哈希值和正确的公共基础路径来决定最终的文件路径,并将小于 8KiB 的资源内联,以减少 HTTP 请求的数量。
23.10 URL转换规则
-
如果 URL 是一个绝对路径 (例如
/images/foo.png
),它将会被保留不变。 -
如果 URL 以
.
开头,它会作为一个相对模块请求被解释且基于你的文件系统中的目录结构进行解析。 -
如果 URL 以
~
开头,其后的任何内容都会作为一个模块请求被解析。这意味着你甚至可以引用 Node 模块中的资源:<img src="~some-npm-package/foo.png">
-
如果 URL 以
@
开头,它也会作为一个模块请求被解析。它的用处在于 Vue CLI 默认会设置一个指向<projectRoot>/src
的别名@
。(仅作用于模版中)
注:上述提到的模块请求,就是nodejs中的require(...)。(因为webpack编译过程中,全程是在nodejs的支持下完成的)。
23.11 webpack配置
在vue中配置webpack有两种方式
- vue.config.js
- webpack-chain
1 在vue.config.js中配置webpack
module.exports = {
configureWebpack: {
plugins: [
new MyAwesomeWebpackPlugin()
]
}
}
该对象会被webpack-merge合并入最终的webpack配置。
2 webpack-chain
module.exports = {
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.tap(options => {
// 修改它的选项...
return options
})
}
}
24 .vuerc
对于@vue/cli的全局配置放在home目录下的.vuerc文件中。
我们可以使用 vue config 命令查看和修改这个文件中的配置。也可以直接使用文本编辑器修改这个文件。
25 vue.config.js
官网:https://cli.vuejs.org/zh/config/
这个配置文件放在项目根目录下,他会被@vue/cli-service自动加载。
我们也可以使用package.json中的vue字段进行配置。
在vue.config.js中,我们导出一个包含配置项的对象
module.exports = {
// 选项...
}
待续...