1.环境安装
参考《官方》官方:
Node 版本要求:
Vue CLI 需要 Node.js 8.9 或更高版本 (推荐 8.11.0+)。你可以使用 nvm 或 nvm-windows 在同一台电脑中管理多个 Node 版本。
使用docker避免污染主机环境,方便迁移。
a.制作运行时docker环境
运行环境需要能及时查看web端页面,使用nginx作为基础镜像:
# Base images nginx.openresty latest
FROM openresty/openresty:stretch
MAINTAINER cffycls@foxmail.com
ENV RESOURCE "\
deb http://mirrors.163.com/debian/ stretch main non-free contrib \n\
deb http://mirrors.163.com/debian/ stretch-updates main non-free contrib \n\
deb http://mirrors.163.com/debian/ stretch-backports main non-free contrib \n\
deb-src http://mirrors.163.com/debian/ stretch main non-free contrib \n\
deb-src http://mirrors.163.com/debian/ stretch-updates main non-free contrib \n\
deb-src http://mirrors.163.com/debian/ stretch-backports main non-free contrib \n\
deb http://mirrors.163.com/debian-security/ stretch/updates main non-free contrib \n\
deb-src http://mirrors.163.com/debian-security/ stretch/updates main non-free contrib \
"
# node + webpack + cnpm
COPY node.tgz /tmp
RUN echo $RESOURCE > /etc/sources.list && cat /etc/sources.list \
&& mkdir -p /usr/local/node && cd /usr/local/node \
&& tar -xf /tmp/node.tgz --strip-components 1 \
&& sed -i '$a export NODE_PATH=/usr/local/node/bin/' /etc/profile \
&& sed -i '$a export PATH=$PATH:$NODE_PATH' /etc/profile \
&& . /etc/profile && echo $PATH \
#设置npm仓库镜像为国内淘宝镜像
&& npm config set registry https://registry.npm.taobao.org \
&& npm install -g cnpm --registry=https://registry.npm.taobao.org \
&& ln -s /usr/local/node/lib/node_modules/cnpm/bin/* /usr/local/bin \
#全局安装 webpack
&& cnpm install -g webpack \
&& cnpm install -g @vue/cli && vue --version
WORKDIR /data
官方vue ui默认端口是8000,启动环境:
[]:~/tmp/dk/vuejs# vim dks.sh
#/bin/bash
docker run -it --name vj -p 8000:8000 -v /root/tmp/dk/vuejs:/data --rm cffycls/vuejs:0.3 bash
需自启动的服务,可以框架搭建好之后,再行添加。
b.启动远程访问
创建一个项目 vue create hello-world ,容器中无对话框,完成后,按照提示 cd hello-world && npm run serve ,显示8080端口、这里修改成上面的8000。
使用 vue ui 命令启动UI界面操作,看到 Starting GUI...,
grep -rn "Starting GUI..." /usr/local/node/lib
查到是这里
/usr/local/node/lib/node_modules/@vue/cli/lib
错误示范。开发一般是本地提交到远程,所以在本地建立项目即可,后期的远程访问可以修改根目录的配置文件。
在项目起始文件夹地址栏,输入cmd进入命令行,运行vue ui等待初始化进入vue项目仪表盘、新建一个project。在仪表盘看到“安装 devtools”,开发调试用。
c.下载浏览器插件
参考《谷歌浏览器插件安装Vue插件 ,Vue Devtools》
git clone https://github.com/vuejs/vue-...,cd vue-devtools,执行npm install 或者有淘宝版的执行 cnpm install,npm run-script build ,插件制作完成。
打开浏览器地址加载
#谷歌:
chrome://extensions/
#360极速:
chrome://myextensions/extensions
刷新 http://localhost:8000/dashboard 页面,加载成功。
d.写个简单的代码
官网下载vue.js开发版,第一串代码:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue-start-1</title>
<script type="text/javascript" src="vue.js"></script>
</head>
<body>
<div id="app">{{message}}</div>
<dev>{{message}}</dev>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
message: 'Hello Vue!'
}
});
setTimeout(function () {
vm.$data.message = "hi world";
}, 2000);
</script>
</body>
</html>
数据对象 <==> dom模型的渲染,实时数据
[Vue warn]: Unknown custom element: <dev> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
2.官网介绍:基础
先敲官网的示例代码。
a.实例介绍代码
HTML部分:
<!--1 起步-->
<dev>{{message}}</dev>
<input type="txet" v-model="inputVal" />{{inputVal}}
<button v-on:click="handleBtnClick">提交</button>
<ul>
<li v-for="item in list">{{item}}</li>
</ul>
<!--2 声明式渲染--->
<span v-bind:title="message2">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
<!--3 条件与循环-->
<p v-if="seen">现在你看到我了</p>
<!--4 -->
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
<!--5 处理用户输入-->
<p>{{ message5 }}</p>
<button v-on:click="reverseMessage">反转消息</button>
<!--6-->
<p>{{ message6 }}</p>
<input v-model="message6">
<!--7 组件化应用构建-->
<ol>
<!-- 创建一个 todo-item 组件的实例 -->
<todo-item>4555</todo-item>
</ol><ol>
<!--
现在我们为每个 todo-item2 提供 todo 对象
todo 对象是变量,即其内容可以是动态的。
我们也需要为每个组件提供一个“key”,稍后再
作详细解释。
-->
<todo-item2
v-for="item in groceryList"
v-bind:todo="item"
v-bind:key="item.id"
></todo-item2>
</ol>
<!--8 7的目标-组件分离、组合
<app-nav></app-nav>
<app-view>
<app-sidebar></app-sidebar>
<app-content></app-content>
</app-view>
-->
<p>
与自定义元素的关系<br />
你可能已经注意到 Vue 组件非常类似于自定义元素——它是 Web 组件规范的一部分,这是因为 Vue 的组件语法部分参考了该规范。例如
Vue 组件实现了 Slot API 与 is 特性。但是,还是有几个关键差别:
Web Components 规范已经完成并通过,但未被所有浏览器原生实现。目前 Safari 10.1+、Chrome 54+ 和 Firefox 63+ 原生支持
Web Components。相比之下,Vue 组件不需要任何 polyfill,并且在所有支持的浏览器 (IE9 及更高版本) 之下表现一致。必要时,
Vue 组件也可以包装于原生自定义元素之内。
Vue 组件提供了纯自定义元素所不具备的一些重要功能,最突出的是跨组件数据流、自定义事件通信以及构建工具集成。
虽然 Vue 内部没有使用自定义元素,不过在应用使用自定义元素、或以自定义元素形式发布时,依然有很好的互操作性。Vue CLI 也支
持将 Vue 组件构建成为原生的自定义元素。
</p>
</div>
js部分:
// 定义名为 todo-item 的新组件
Vue.component('todo-item', {
template: '<li>这是个待办项</li>'
});
Vue.component('todo-item2', {
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
});
var vm = new Vue({
el: "#app",
data: {
list: [],
message: "hellow world",
message2: '页面加载于 ' + new Date().toLocaleString(),
inputVal: '123',
seen: true,
todos: [
{ text: '学习 JavaScript' },
{ text: '学习 Vue' },
{ text: '整个牛项目' }
],
message5: 'Hello Vue.js!',
message6: 'Hello Vue!',
groceryList: [
{ id: 0, text: '蔬菜' },
{ id: 1, text: '奶酪' },
{ id: 2, text: '随便其它什么人吃的东西' }
]
},
methods: {
handleBtnClick: function () {
console.log(this.$data.inputVal)
this.list.push(this.$data.inputVal)
this.inputVal = ''
},
reverseMessage: function () {
this.message5 = this.message5.split('').reverse().join('')
}
}
});
- el绑定渲染的dom根,操作空间;
- data对应操作空间内的数据/模型集合,动态同步,v-if="真假"显示隐藏,v-for="val in array";
- methods调用的方法集合,v-on:xx-event="xx-method",方法中this.数据对象<==>vm.data.数据元素;
- 组件先注册,在new Vew()启动渲染。
b.数据与方法
对象属性/模型值绑定,这时 vm.a 追踪 data.a 的变化:
// 我们的数据对象
var data = { a: 1 }
// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
data: data
})
Object.freeze(obj)会阻止修改现有的属性,也意味着响应系统无法再追踪变化。
c.生命周期
生命周期钩子
new Vue({
data: {
a: 1
},
//created 钩子可以用来在一个实例被创建之后执行代码:
created: function () {
// `this` 指向 vm 实例
console.log('a is: ' + this.a) // => "a is: 1"
}
})
其它的钩子: mounted、updated 和 destroyed。生命周期钩子的 this 上下文指向调用它的 Vue 实例。
d.模板语法
模板匹配语法介绍参看: 小花喵《mustache语法》。
- 原始文本:标签无v-开头属性
<span>Message: {{ msg }}</span>
- 不更新文本:v-once
<span v-once>这个将不会改变: {{ msg }}</span>
- HTML源码:v-html
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p> - 使用JavaScript 表达式
{{ message.split('').reverse().join('') }}
- 指令
指令特性的值预期是单个 JavaScript 表达式 (v-for 是例外),如:
<p v-if="seen">现在你看到我了</p>这里,v-if 指令将根据表达式 seen 的值的真假来插入/移除 <p> 元素。 - 参数接收
一个参数:<a v-bind:href="url">...</a>
<a v-on:click="doSomething">...</a> - 修饰符:
<form v-on:submit.prevent="onSubmit">...</form>
触发的事件调用 event.preventDefault() - 缩写
<!-- 完整语法 --> <a v-bind:href="url">...</a>
<!-- 缩写 --> <a :href="url">...</a>
v-on 缩写
<!-- 完整语法 --> <a v-on:click="doSomething">...</a>
<!-- 缩写 --> <a @click="doSomething">...</a>
e.计算属性
对于任何复杂逻辑,你都应当使用计算属性
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
高级用例:提供的函数将用作属性 vm.reversedMessage 的 getter 函数:
<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('')
}
}
})
计算属性缓存 vs 方法:你可能已经注意到我们可以通过在表达式中调用方法来达到同样的效果:
<p>Reversed message: "{{ reversedMessage() }}"</p>
计算属性是基于它们的响应式依赖进行缓存的,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
计算属性 vs 侦听属性:同理
#侦听属性
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]
}
}
}
f.侦听器
https://cn.vuejs.org/v2/guide...
<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>
3.进阶:样式控制
a.Class 与 Style 绑定
<div
class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>
#===================================#
//js部分
data: {
isActive: true,
hasError: false
}
结果渲染为:<div class="static active"></div>
- 对象语法
或者,绑定的数据对象不必内联定义在模板里:
<div v-bind:class="classObject"></div>
#===================================#
//js部分
data: {
classObject: {
active: true,
'text-danger': false
}
}
#------------------------------------#
data: {
isActive: true,
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
- 数组语法
我们可以把一个数组传给 v-bind:class,以应用一个 class 列表:
<div v-bind:class="[activeClass, errorClass]"></div>
//#===================================#
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
//#------------------------------------#
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div> - 自动添加前缀:v-bind:style
这样写只会渲染数组中最后一个被浏览器支持的值。
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
b.条件渲染
v-else-if,顾名思义,充当 v-if 的“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 管理可复用的元素,如用户在不同的登录方式之间切换:
<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>
添加一个具有唯一值的 key 属性,每次切换时,输入框都将被重新渲染。
c.列表渲染
- 父级和子级参数
<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' }
]
}
})
- 对象里使用 v-for
<li v-for="value in object">
{{ value }}
</li>
//#===================================#
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
//#===================================#
<li v-for="(value, name) in object">
{{ name }}: {{ value }}
</li>
- 维护状态
当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素。
就地排序、追踪绑定key:
<div v-for="item in items" v-bind:key="item.id">
<!-- 内容 -->
</div>
d.数组更新检测
push() pop() shift() unshift() splice() sort() reverse()
替换数组
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
由于 JavaScript 的限制,Vue 不能检测以下数组的变动:
当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength
Vue.set(vm.items, indexOfItem, newValue)
//你也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:
vm.$set(vm.items, indexOfItem, newValue)
//为了解决第二类问题,你可以使用 splice:
vm.items.splice(newLength)
e.对象变更检测
- 非根级、全局set方法
还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` 现在是响应式的
vm.b = 2
// `vm.b` 不是响应式的
对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, 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)
f.显示过滤/排序
- 计算属性处理
<li v-for="n in evenNumbers">{{ n }}</li>
//#===================================#
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
evenNumbers: function () {
return this.numbers.filter(function (number) {
return number % 2 === 0
})
}
}
- 普通event方法处理
<li v-for="n in even(numbers)">{{ n }}</li>
//#===================================#
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
even: function (numbers) {
return numbers.filter(function (number) {
return number % 2 === 0
})
}
}
- v-for范围连续打印
可以对<template>标签使用
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
//1 2 3 4 5 6 7 8 9 10
//当它们处于同一节点,v-for 的优先级比 v-if 更高。只渲染未完成的 todo:
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
4.事件处理、表单输入
a.监听事件
- 变量监听和监听方法
<div id="example-1">
<button v-on:click="counter += 1">Add 1</button>
<p>The button above has been clicked {{ counter }} times.</p>
<button v-on:click="greet">Greet</button>
</div>
//#===================================#
var example1 = new Vue({
el: '#example-1',
data: {
counter: 0
},
// 在 `methods` 对象中定义方法
methods: {
greet: function (event) {
// `this` 在方法里指向当前 Vue 实例
alert('Hello ' + this.name + '!')
// `event` 是原生 DOM 事件
if (event) {
alert(event.target.tagName)
}
}
}
})
- 原始的 DOM 事件 $event
<button v-on:click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
//#===================================#
methods: {
warn: function (message, event) {
// 现在我们可以访问原生事件对象
if (event) event.preventDefault()
alert(message)
}
}
b.事件修饰符
- xxevent.下面属性
.stop .prevent阻碍 .capture捕获 .self .once .passive被动的
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 ,提升移动端的性能 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
c.按键修饰符
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">
<input v-on:keyup.page-down="onPageDown">
<!-- 在上述示例中,处理函数只会在 $event.key 等于 PageDown 时被调用。 -->
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
你还可以通过全局 config.keyCodes 对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
- 系统修饰键
.ctrl
.alt
.shift
.meta 徽标键 (⌘、⊞、◆、META)
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
.exact 修饰符 https://cn.vuejs.org/v2/guide...
d.表单输入绑定
- 文本输入
<!-- 单行 -->
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p><span>Multiline message is:</span>
<!-- 多行文本,注意不是<textarea>{{text}}</textarea>这样的写法 -->
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>
- 复选框、单选按钮组
绑定到数组
<div id='example-3'>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
</div>
//#===================================#
new Vue({
el: '#example-3',
data: {
checkedNames: []
}
})
单选同理,type="radio"
- 下拉选择框单选、多选
<div id="example-5">
<select v-model="selected"> <!-- *多选时加 multiple -->
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
//#===================================#
data: {
selected: '' //*多选填数组 []
}
用 v-for 渲染的动态选项:
<select v-model="selected">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<span>Selected: {{ selected }}</span>
//#===================================#
data: {
selected: 'A',
options: [
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
]
}
e.值绑定
复选框值绑定(确认某项是否被选中);单选按钮同理,输出指定值
<input
type="checkbox"
v-model="toggle"
true-value="yes"
false-value="no"
>
//#===================================#
// 当选中时
vm.toggle === 'yes'
// 当没有选中时
vm.toggle === 'no'
//#===================================#
<input type="radio" v-model="pick" v-bind:value="a">
可以绑定为内联对象字面量
<option v-bind:value="{ number: 123 }">123</option>
f.修饰符
- .lazy: input 事件同步变为 change 事件同步
- .number:数值转化,<input v-model.number="age" type="number">
- .trim:首尾空白字符过滤,<input v-model.trim="msg">
小结
代码是敲出来的,这些是一些基础的规则和用例。继续操练代码。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。