一、Vue2
Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。Vue.js是一个MVVM模式的框架。
1、vue2需掌握的几方面
- 网络通信 : axios(不属于Vue框架,单独的一套前端通信框架,负责与服务器交互。)
- 页面跳转 : vue-router
- 状态管理:vuex
- Vue-UI : ICE , Element UI ( npm 安装
推荐使用 npm 的方式安装,它能更好地和 webpack 打包工具配合使用。
npm i element-ui -S
) - css:SASS(基于Ruby)、LESS(基于NodeJS)
- WebPack:模块打包器,主要作用就是打包、压缩、合并及按序加载
- NPM:项目综合管理工具,类似于Maven
- YARN:NPM的替代方案,类似于Maven和Gradle的关系
2、Vue2入门语法
初始vue模板:
<div id="app" v-once> <!--v-once标签添加后,其标签下内容不发生改变 -->
{{ message }} --双大括号文本插值
</div>
new一个Vue对象,绑定div ‘#app’ 到 el 属性,data属性中给定默认值message,修改message的值,页面上的值也会发生改变。如果加入v-once,其值不会改变。
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
app.message='change!!';
2-1 条件 v-if
v-if: vue的条件判断,当seen为ture,p标签显示。
<div id="app-3">
<p v-if="seen">现在你看到我了</p>
</div>
var app3 = new Vue({
el: '#app-3',
data: {
seen: true
}
})
app3.seen = false;
- v-else
- v-else-if
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
用 key 管理可复用的元素
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
2-2 循环 v-for
v-for: vue的循环标签,todos传参为数组对象,todo为遍历出的对象,可以通过 todo.text 取出对象的属性值。
<div id="app-4">
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
</div>
var app4 = new Vue({
el: '#app-4',
data: {
todos: [
{ text: '学习 JavaScript' },
{ text: '学习 Vue' },
{ text: '整个牛项目' }
]
}
})
在 v-for 块中,我们可以访问所有父作用域的 property。v-for 还支持一个可选的第二个参数,即当前项的索引。
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
var example2 = new Vue({
el: '#example-2',
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
结果:
- Parent - 0 - Foo
- Parent - 1 - Bar
也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法:
<div v-for="item of items"></div>
2-3 事件监听 v-on
v-on: vue的事件监听器,监听事件方法,点击按钮时,触发reverseMessage方法,反转按钮名称。
缩写:v-on: 等于 @
<div id="app-5">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">反转消息</button>
<button @click="reverseMessage">反转消息</button>
</div>
var app5 = new Vue({
el: '#app-5',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
2-4 双向绑定 v-model
v-model: vue提供的双向绑定指令,实现表单输入和应用状态之间的双向绑定。
<div id="app-6">
<p>{{ message }}</p>
<input v-model="message">
</div>
var app6 = new Vue({
el: '#app-6',
data: {
message: 'Hello Vue!'
}
})
app6.message='change!!';
2-4 组件化应用构建
组件本质上是一个拥有预定义选项的一个 Vue 实例。在 Vue 中注册组件很简单:
// 定义名为 todo-item 的新组件
Vue.component('todo-item', {
template: '<li>这是个待办项</li>'
})
var app = new Vue(...)
现在你可以用它构建另一个组件模板:
<ol>
<!-- 创建一个 todo-item 组件的实例 -->
<todo-item></todo-item>
</ol>
但是这样会为每个待办项渲染同样的文本,这看起来并不炫酷。我们应该能从父作用域将数据传到子组件才对。让我们来修改一下组件的定义,使之能够接受一个 prop:
Vue.component('todo-item', {
// todo-item 组件现在接受一个
// "prop",类似于一个自定义 attribute。
// 这个 prop 名为 todo。
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
现在,我们可以使用 v-bind 指令将待办项传到循环输出的每个组件中:
<div id="app-7">
<ol>
<!--
现在我们为每个 todo-item 提供 todo 对象
todo 对象是变量,即其内容可以是动态的。
我们也需要为每个组件提供一个“key”,稍后再
作详细解释。
-->
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
:key="item.id"
></todo-item>
</ol>
</div>
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
var app7 = new Vue({
el: '#app-7',
data: {
groceryList: [
{ id: 0, text: '蔬菜' },
{ id: 1, text: '奶酪' },
{ id: 2, text: '随便其它什么人吃的东西' }
]
}
})
2-5 原始 HTML
v-html:双大括号会将数据解释为普通文本,而非 HTML 代码,而v-html会将数据解释为html代码。
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
2-6 Attribute
Mustache 语法不能作用在 HTML attribute 上,遇到这种情况应该使用 v-bind 指令:
<div v-bind:id="dynamicId"></div>
对于布尔 attribute (它们只要存在就意味着值为 true),v-bind 工作起来略有不同,在这个例子中:
<button v-bind:disabled="isButtonDisabled">Button</button>
如果 isButtonDisabled 的值是 null、undefined 或 false,则 disabled attribute 甚至不会被包含在渲染出来的 元素中。
缩写:v-bind: 等于 :
从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:
<!--
注意,参数表达式的写法存在一些约束,如之后的“对动态参数表达式的约束”章节所述。
-->
<a v-bind:[attributeName]="url"> ... </a>
这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data property attributeName,其值为 “href”,那么这个绑定将等价于 v-bind:href。
同样地,你可以使用动态参数为一个动态的事件名绑定处理函数:
<a v-on:[eventName]="doSomething"> ... </a>
2-6-1 对象语法
我们可以传给 v-bind:class 一个对象,以动态地切换 class:
<div v-bind:class="{ active: isActive }"></div>
上面的语法表示 active 这个 class 存在与否将取决于数据 property isActive 的 truthiness。
你可以在对象中传入更多字段来动态切换多个 class。此外,v-bind:class 指令也可以与普通的 class attribute 共存。
<div
class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>
data: {
isActive: true,
hasError: false
}
当 isActive 或者 hasError 变化时,class 列表将相应地更新。例如,如果 hasError 的值为 true,class 列表将变为 “static active text-danger”。
绑定的数据对象不必内联定义在模板里:
<div v-bind:class="classObject"></div>
data: {
classObject: {
active: true,
'text-danger': false
}
}
渲染的结果和上面一样。我们也可以在这里绑定一个返回对象的计算属性。这是一个常用且强大的模式:
<div v-bind:class="classObject"></div>
data: {
isActive: true,
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
2-6-2 数组语法
我们可以把一个数组传给 v-bind:class,以应用一个 class 列表:
<div v-bind:class="[activeClass, errorClass]"></div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
如果你也想根据条件切换列表中的 class,可以用三元表达式:
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
这样写将始终添加 errorClass,但是只有在 isActive 是 truthy 1 时才添加 activeClass。
不过,当有多个条件 class 时这样写有些繁琐。所以在数组语法中也可以使用对象语法:
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
2-7 使用 JavaScript 表达式
对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 。你不应该在模板表达式中试图访问用户定义的全局变量。
2-8 修饰符
修饰符 (modifier) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():
<form v-on:submit.prevent="onSubmit">...</form>
在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。
- .stop
- .prevent
- .capture
- .self
- .once
- .passive
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。
为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
你还可以通过全局 config.keyCodes 对象自定义按键修饰符别名
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
2-9 控制元素显示隐藏 v-show
用于根据条件展示元素的选项 v-show 指令:
h1 v-show="ok">Hello!</h1>
不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS property display。
注意,v-show 不支持 < template > 元素,也不支持 v-else。
3、Vue对象详解
3-1 数据与方法
当一个 Vue 实例被创建时,它将 data 对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
// 我们的数据对象
var data = { a: 1 }
// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
data: data
})
// 获得这个实例上的 property
// 返回源数据中对应的字段
vm.a == data.a // => true
// 设置 property 也会影响到原始数据
vm.a = 2
data.a // => 2
// ……反之亦然
data.a = 3
vm.a // => 3
当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于 data 中的 property 才是响应式的。也就是说如果你添加一个新的 property,比如: vm.b = ‘hi’ ;那么对 b 的改动将不会触发任何视图的更新。如果你知道你会在晚些时候需要一个 property,但是一开始它为空或不存在,那么你仅需要设置一些初始值。比如:
data: {
newTodoText: '',
visitCount: 0,
hideCompletedTodos: false,
todos: [],
error: null
}
这里唯一的例外是使用 Object.freeze(),这会阻止修改现有的 property,也意味着响应系统无法再追踪变化。
var obj = {
foo: 'bar'
}
Object.freeze(obj) //之后修改obj,系统无法追踪其变化
new Vue({
el: '#app',
data: obj
})
<div id="app">
<p>{{ foo }}</p>
<!-- 这里的 `foo` 不会更新! -->
<button v-on:click="foo = 'baz'">Change it</button>
</div>
Vue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀 $,以便与用户定义的 property 区分开来。例如:
var data = { a: 1 }
var vm = new Vue({
el: '#example',
data: data
})
vm.$data === data // => true
vm.$el === document.getElementById('example') // => true
// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
// 这个回调将在 `vm.a` 改变后调用
})
3-2 实例生命周期钩子
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
- created
- mounted
- updated
- destroyed
//例如:
new Vue({
data: {
a: 1
},
created: function () {
// `this` 指向 vm 实例
console.log('a is: ' + this.a)
}
})
// => "a is: 1"
tip:生命周期钩子的 this 上下文指向调用它的 Vue 实例,不要在选项 property 或回调上使用箭头函数,因为箭头函数并没有 this,this 会作为变量一直向上级词法作用域查找,直至找到为止,经常导致 Uncaught TypeError: Cannot read property of undefined 或 Uncaught TypeError: this.myMethod is not a function 之类的错误。
bind:只调用一次,第一次绑定到父元素时,父节点不存在,常用于对dom元素进行初始化(特指样式)或者事件的绑定
inserted:插入父节点调用,父节点存在,可以获取设置dom的值,常用于编写初始化业务逻辑(特指js行为)
update:被绑定元素更新的时候调用,常用于编写主逻辑
unbind:解绑调用,常用于解绑事件removeEventListeren和删除元素delete el.xxx
bind在组件的created之后执行,而inserted在组件的mounted之后执行,所以bind时的el.parentElement为null,而inserted存在
innerHTML也可以再bind中进行初始化获取修改
如果需要传值多个可以传一个对象
update后将会触发当前组件的下所有使用该指令的componentUpdated和update钩子,
update指令在但是可能发生在其子 VNode 更新之前,componentUpdated指令所在组件的 VNode 及其子 VNode 全部更新后调用,举个例子,当我们更改指令绑定元素的innerHTML时,在update获取的是修改之前的,在componentUpdated获取的是修改之后的
3-3 计算属性缓存 computed
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
结果:
Original message: “Hello”
Computed reversed message: “olleH”
这里我们声明了一个计算属性 reversedMessage。我们提供的函数将用作 property vm.reversedMessage 的 getter 函数:
console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'
你可以打开浏览器的控制台,自行修改例子中的 vm。vm.reversedMessage 的值始终取决于 vm.message 的值。
你可以像绑定普通 property 一样在模板中绑定计算属性。Vue 知道 vm.reversedMessage 依赖于 vm.message,因此当 vm.message 发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新。而且最妙的是我们已经以声明的方式创建了这种依赖关系:计算属性的 getter 函数是没有副作用 (side effect) 的,这使它更易于测试和理解。
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
这也同样意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:
computed: {
now: function () {
return Date.now()
}
}
相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
如果你不希望有缓存,请用方法来替代。
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。细想一下这个例子:
div id="demo">{{ fullName }}</div>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
上面代码是命令式且重复的。将它与计算属性的版本进行比较:
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
计算属性默认只有 getter,不过在需要时你也可以提供一个 setter:
// ...
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
// ...
现在再运行 vm.fullName = ‘John Doe’ 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。
3-4 侦听器 watch
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
// `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
// AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
// `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
// 请参考:https://lodash.com/docs#debounce
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
},
methods: {
getAnswer: function () {
if (this.question.indexOf('?') === -1) {
this.answer = 'Questions usually contain a question mark. ;-)'
return
}
this.answer = 'Thinking...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
}
}
})
</script>
在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
除了 watch 选项之外,您还可以使用命令式的 vm.$watch API。
3-5 插槽
在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slot 和 slot-scope 这两个目前已被废弃但未被移除且仍在文档中的 attribute。新语法的由来可查阅这份 RFC。
Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 < slot> 元素作为承载分发内容的出口。
它允许你像这样合成组件:
<navigation-link url="/profile">
Your Profile
</navigation-link>
然后你在 < navigation-link > 的模板中可能会写为:
<a
v-bind:href="url"
class="nav-link"
>
<slot></slot>
</a>
当组件渲染的时候,< slot></ slot> 将会被替换为“Your Profile”。插槽内可以包含任何模板代码,包括 HTML:
<navigation-link url="/profile">
<!-- 添加一个 Font Awesome 图标 -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
甚至其它的组件:
<navigation-link url="/profile">
<!-- 添加一个图标的组件 -->
<font-awesome-icon name="user"></font-awesome-icon>
Your Profile
</navigation-link>
如果 < navigation-link > 的 template 中没有包含一个 < slot > 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
当你想在一个插槽中使用数据时,例如:
<navigation-link url="/profile">
Logged in as {{ user.name }}
</navigation-link>
插槽跟模板的其它地方一样可以访问相同的实例 property (也就是相同的“作用域”),而不能访问 < navigation-link> 的作用域。例如 url 是访问不到的:
<navigation-link url="/profile">
Clicking here will send you to: {{ url }}
<!--
这里的 `url` 会是 undefined,因为其 (指该插槽的) 内容是
_传递给_ <navigation-link> 的而不是
在 <navigation-link> 组件*内部*定义的。
-->
</navigation-link>
作为一条规则,请记住:
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
4、vue路由 vue-router
如果你只需要非常简单的路由而不想引入一个功能完整的路由库,可以像这样动态渲染一个页面级的组件:
const NotFound = { template: '<p>Page not found</p>' }
const Home = { template: '<p>home page</p>' }
const About = { template: '<p>about page</p>' }
const routes = {
'/': Home,
'/about': About
}
new Vue({
el: '#app',
data: {
currentRoute: window.location.pathname
},
computed: {
ViewComponent () {
return routes[this.currentRoute] || NotFound
}
},
render (h) { return h(this.ViewComponent) }
})
结合 HTML5 History API,你可以建立一个麻雀虽小但是五脏俱全的客户端路由器。可以直接看实例应用。
5、webpack基本使用
5-1 创建项目目录并初始化
创建项目,并打开项目所在目录的终端,输入命令:
npm init -y
5-2 创建首页及js文件
在项目目录中创建index.html页面
在项目目录中创建js文件夹,并在文件夹中创建index.js文件
安装jQuery
打开项目目录终端,输入命令:
npm install jQuery -S
导入jQuery
打开index.js文件,编写代码导入jQuery并实现功能:
import $ from "jquery";
$(function(){
$("li:odd").css("background","cyan");
$("li:odd").css("background","pink");
})
运行发现:
报错 因为import $ from “jquery”;这句代码属于ES6的新语法代码,在浏览器中可能会存在兼容性问题。
安装配置webpack
1、输入如下代码
npm install webpack webpack-cli -D
2、在项目根目录中,创建一个 webpack.config.js 的配置文件
在 webpack.config.js 文件中编写代码进行webpack配置,如下:
module.exports = {
mode:"development"//可以设置为development(开发模式),production(发布模式)
}
3、修改项目中的package.json文件添加运行脚本dev,如下:
"scripts":{
"dev":"webpack"
}
4、运行 npm run dev 命令进行项目打包,并在页面中引入项目打包生成的js文件(即为在 dist 路径下生成的 main.js)
配置打包的入口与出口
默认打包方式
将src/index.js 作为默认的打包入口js文件
将dist/main.js 作为默认的打包输出js文件
配置打包方式
--dirname 表示当前文件的绝对路径
const path = require("path");
module.exports = {
mode:"development",
//设置入口文件路径
entry: path.join(__dirname,"./src/xx.js"),
//设置出口文件
output:{
//设置路径
path:path.join(__dirname,"./dist"),
//设置文件名
filename:"res.js"
}
}
配置自动打包功能
原因: 在修改代码后需要 重新运行 npm run dev
1、安装自动打包的包
npm install webpack-dev-server -D
2、修改package.json中的dev指令如下:
"scripts":{
"dev":"webpack-dev-server"
}
3、将引入的js文件路径更改为:
<script src="/bundle.js"></script>
4、运行npm run dev,进行打包
5、打开网址查看效果:http://localhost:8080
注意:
1、webpack-dev-server会启动个实时打包的http服务器
2、webpack-dev- server打包生成的输出文件,默认放到了项目根目录中,而且是虚拟的、看不见的
配置html-webpack-plugin
此为预览插件
使用 npm install html-webpack-plugin -D 安装预览功能包
1、安装默认预览功能的包:html-webpack-plugin
npm install html-webpack-plugin -D
2、修改webpack.config.js文件,如下:
//导入包
const HtmlWebpackPlugin = require("html-webpack-plugin");
//创建对象
const htmlPlugin = new HtmlWebpackPlugin({
//设置生成预览页面的模板文件
template:"./src/index.html",
//设置生成的预览页面名称
filename:"index.html"
})
3、继续修改webpack.config.js文件,添加plugins信息:
module.exports = {
......
plugins:[ htmlPlugin ]
}
配置自动打开浏览器页面
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --open --host 127.0.0.1 --port 8888",
"build": "webpack -p"
},
Webpack 加载器
loader 调用过程图示
css 打包器
1、 安装 style-loader 安装包处理样式文件
npm install style-loader css-loader -D
2、 在webpack.config.js 的module 的rules 数组中配置
plugins: [htmlPlguin, new VueLoaderPlugin()],
module: {
rules: [
{ test: /\.css$/, use: ['style-loader', 'css-loader', 'postcss-loader'] },
{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
{ test: /\.jpg|png|gif|bmp|ttf|eot|svg|woff|woff2$/, use: 'url-loader?limit=16941' },
{ test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ },
{ test: /\.vue$/, use: 'vue-loader' }
]
}
3、 在 index.js 导入相应配置
import './css/1.css'
注意:
1、 use 数组中指定的 loader 顺序是固定的
2、 多个loader 的调用顺序是从后往前调
less 打包器
1、运行如下命令
npm i less-loader less -D
2、 在webpack.config.js 的module 的rules 数组中配置
{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
3、 在 index.js 导入相应配置
配置 postCSS 自动添加 css 的兼容前缀
目的: 解决各个浏览器的兼容问题
1、运行 npm i postcss-loader autoprefixer -D
命令
2、在项目根目录中创建 postcss 的配置文件 postcss.config.js 并初始化如下配置
const autoprefixer = require('autoprefixer')
module.exports = {
plugins: [autoprefixer]
}
3、在webpack.config.js 修改css 的loader 的规则如下:
{ test: /\.css$/, use: ['style-loader', 'css-loader', 'postcss-loader'] },
打包样式表中的图片和字体文件
1、运行 npm install url-loader file-loader -D
2、更改webpack.config.js的module中的rules数组
module.exports = {
......
plugins:[ htmlPlugin ],
module : {
rules:[
{
//test设置需要匹配的文件类型,支持正则
test:/\.css$/,
//use表示该文件类型需要调用的loader
use:['style-loader','css-loader']
},
{
test:/\.less$/,
use:['style-loader','css-loader','less-loader']
},
{
test:/\.scss$/,
use:['style-loader','css-loader','sass-loader']
},{
test:/\.jpg|png|gif|bmp|ttf|eot|svg|woff|woff2$/,
//limit用来设置字节数,只有小于limit值的图片,才会转换
//为base64图片
use:"url-loader?limit=16940"
}
]
}
}
注意:
1、其中 ? 之后的是 loader 的参数项
2、limit 用来指定图片的大小,单位是字节(byte),只有limit
大小的图片,才能被转换为 base64 图片
打包处理JS中高级语法
我们需要安装babel系列的包
1、安装babel转换器
npm install babel-loader @babel/core @babel/runtime -D
2、安装babel语法插件包
npm install @babel/preset-env @babel/plugin-transform-runtime @babel/plugin-proposal-class-properties -D
3、在项目根目录创建并配置babel.config.js文件
module.exports = {
presets:["@babel/preset-env"],
plugins:[ "@babel/plugin-transform-runtime", "@babel/plugin-proposal-class-properties" ]
}
4、配置规则:更改webpack.config.js的module中的rules数组
module.exports = {
......
plugins:[ htmlPlugin ],
module : {
rules:[
{
//test设置需要匹配的文件类型,支持正则
test:/\.css$/,
//use表示该文件类型需要调用的loader
use:['style-loader','css-loader',exclude : /node_modules/]
},
Vue 单文件组件
传统组件存在的问题 :
1、全局定义的组件不能重名
2、字符串模板缺乏语法高亮,在HTML多行时需要
3、不支持css(当html和js组件化时,css没有参与其中
4、没有构建步骤限制,只能使用H5和ES5,不能使用预处理器(babel)
单文件的组成结构
template 组件的模板区域
script 业务逻辑区域
style 样式区域
代码:
<template>
//组件代码区域
<div>
<h1>这是App.vue 根组件</h1>
</div>
</template>
<script>
//js代码区域
export
</script>
<style scoped>
样式代码区域
</style>
导入单文件组件
import App Vue from ' ./components/App.vue'
webpack配置vue 组件加载器
1、安装vue组件的加载器
npm install vue-loader vue-template-compiler -D
2、配置规则:更改webpack.config.js的module中的rules数组
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const vuePlugin = new VueLoaderPlugin();
module.exports = {
......
plugins:[ htmlPlugin, vuePlugin ],
module : {
rules:[
...//其他规则
{
test:/\.vue$/,
loader:"vue-loader",
}
]
}
}
在webpack项目中使用 vue
将vue单文件组件能够使用,我们必须要安装vue,并使用vue来引用vue单文件组件。
1、安装Vue npm install vue -S
2、在index.js中引入vue:import Vue from “vue”
3、创建Vue实例对象并指定el,最后使用render函数渲染单文件组件
注意:
建议使用 render 函数 ,而不是template 等模块等,因为导入的是Vue 的简化版,有可能不支持
const vm = new Vue({
el:"#first",
render:h=>h(app)
})
webpack打包发布项目
在项目上线之前,我们需要将整个项目打包并发布。
1、 配置package.json
"scripts":{
"dev":"webpack-dev-server",
"build":"webpack -p"
}
2、在项目打包之前,可以将dist目录删除,生成全新的dist目录
3、在终端运行 npm run build ,进行打包
truthy 不是 true,详见 MDN 的解释。 ↩︎