Vue.js 是目前最火的一个前端框架,React是最流行的一个前端框架(React除了开发网站,还可以开发手机App, Vue语法也是可以用于进行手机App开发的,需要借助于Weex)
Vue.js 是前端的主流框架之一,和Angular.js、React.js 一起,并成为前端三大主流框架!
Vue.js 是一套构建用户界面的框架,只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。(Vue有配套的第三方类库,可以整合起来做大型项目的开发)
前端的主要工作?主要负责MVC中的V这一层;主要工作就是和界面打交道,来制作前端页面效果;
提高开发效率的发展历程:原生JS -> Jquery之类的类库 -> 前端模板引擎 -> Angular.js / Vue.js(能够帮助我们减少不必要的DOM操作;提高渲染效率;双向数据绑定的概念【通过框架提供的指令,我们前端程序员只需要关心数据的业务逻辑,不再关心DOM是如何渲染的了】)
在Vue中,一个核心的概念,就是让用户不再操作DOM元素,解放了用户的双手,让程序员可以更多的时间去关注业务逻辑;
增强自己就业时候的竞争力
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 1. 导入Vue的包 -->
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<!-- 将来 new 的Vue实例,会控制这个 元素中的所有内容 -->
<!-- Vue 实例所控制的这个元素区域,就是我们的 V -->
<div id="app">
<p>{{ msg }}</p>
</div>
<script>
// 2. 创建一个Vue的实例
// 当我们导入包之后,在浏览器的内存中,就多了一个 Vue 构造函数
// 注意:我们 new 出来的这个 vm 对象,就是我们 MVVM中的 VM调度者
var vm = new Vue({
el: '#app', // 表示,当前我们 new 的这个 Vue 实例,要控制页面上的哪个区域
// 这里的 data 就是 MVVM中的 M,专门用来保存 每个页面的数据的
data: { // data 属性中,存放的是 el 中要用到的数据
msg: '欢迎学习Vue' // 通过 Vue 提供的指令,很方便的就能把数据渲染到页面上,程序员不再手动操作DOM元素了【前端的Vue之类的框架,不提倡我们去手动操作DOM元素了】
}
})
</script>
</body>
</html>
基本的代码结构
和插值表达式
、v-cloak
v-text
和v-html
v-bind
的三种用法直接使用指令v-bind
使用简化指令:
在绑定的时候,拼接绑定内容::title="btnTitle + ', 这是追加的内容'"
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<style>
[v-cloak] {
/* display: none; */
}
style>
head>
<body>
<div id="app">
<p v-cloak>++++++++ {{ msg }} ----------p>
<h4 v-text="msg">==================h4>
<div>{{msg2}}div>
<div v-text="msg2">div>
<div v-html="msg2">1212112div>
<input type="button" value="按钮" @click="show">
div>
<script src="./lib/vue-2.4.0.js">script>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: '123',
msg2: '哈哈,我是一个大大的H1, 我大,我骄傲
',
mytitle: '这是一个自己定义的title'
},
methods: { // 这个 methods属性中定义了当前Vue实例所有可用的方法
show: function () {
alert('Hello')
}
}
})
/* document.getElementById('btn').onclick = function(){
alert('Hello')
} */
script>
body>
html>
v-on
和跑马灯效果
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<input type="button" value="浪起来" @click="lang">
<input type="button" value="低调" @click="stop">
<h4>{{ msg }}h4>
div>
<script>
// 注意:在 VM实例中,如果想要获取 data 上的数据,或者 想要调用 methods 中的 方法,必须通过 this.数据属性名 或 this.方法名 来进行访问,这里的this,就表示 我们 new 出来的 VM 实例对象
var vm = new Vue({
el: '#app',
data: {
msg: '猥琐发育,别浪~~!',
intervalId: null // 在data上定义 定时器Id
},
methods: {
lang() {
// console.log(this.msg)
// 获取到头的第一个字符
// this
if (this.intervalId != null) return;
this.intervalId = setInterval(() => {
var start = this.msg.substring(0, 1)
// 获取到 后面的所有字符
var end = this.msg.substring(1)
// 重新拼接得到新的字符串,并赋值给 this.msg
this.msg = end + start
}, 400)
// 注意: VM实例,会监听自己身上 data 中所有数据的改变,只要数据一发生变化,就会自动把 最新的数据,从data 上同步到页面中去;【好处:程序员只需要关心数据,不需要考虑如何重新渲染DOM页面】
},
stop() { // 停止定时器
clearInterval(this.intervalId)
// 每当清除了定时器之后,需要重新把 intervalId 置为 null
this.intervalId = null;
}
}
})
// 分析:
// 1. 给 【浪起来】 按钮,绑定一个点击事件 v-on @
// 2. 在按钮的事件处理函数中,写相关的业务逻辑代码:拿到 msg 字符串,然后 调用 字符串的 substring 来进行字符串的截取操作,把 第一个字符截取出来,放到最后一个位置即可;
// 3. 为了实现点击下按钮,自动截取的功能,需要把 2 步骤中的代码,放到一个定时器中去;
script>
body>
html>
v-on的缩写
和事件修饰符
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<style>
.inner {
height: 150px;
background-color: darkcyan;
}
.outer {
padding: 40px;
background-color: red;
}
style>
head>
<body>
<div id="app">
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {
div1Handler() {
console.log('这是触发了 inner div 的点击事件')
},
btnHandler() {
console.log('这是触发了 btn 按钮 的点击事件')
},
linkClick() {
console.log('触发了连接的点击事件')
},
div2Handler() {
console.log('这是触发了 outer div 的点击事件')
}
}
});
script>
body>
html>
.stop 阻止冒泡
.prevent 阻止默认事件
.capture 添加事件侦听器时使用事件捕获模式
.self 只当事件在该元素本身(比如不是子元素)触发时触发回调
.once 事件只触发一次
v-model
和双向数据绑定
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<h4>{{ msg }}h4>
<input type="text" style="width:100%;" v-model="msg">
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
msg: '大家都是好学生,爱敲代码,爱学习,爱思考,简直是完美,没瑕疵!'
},
methods: {
}
});
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<input type="text" v-model="n1">
<select v-model="opt">
<option value="+">+option>
<option value="-">-option>
<option value="*">*option>
<option value="/">/option>
select>
<input type="text" v-model="n2">
<input type="button" value="=" @click="calc">
<input type="text" v-model="result">
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
n1: 0,
n2: 0,
result: 0,
opt: '+'
},
methods: {
calc() { // 计算器算数的方法
// 逻辑:
/* switch (this.opt) {
case '+':
this.result = parseInt(this.n1) + parseInt(this.n2)
break;
case '-':
this.result = parseInt(this.n1) - parseInt(this.n2)
break;
case '*':
this.result = parseInt(this.n1) * parseInt(this.n2)
break;
case '/':
this.result = parseInt(this.n1) / parseInt(this.n2)
break;
} */
// 注意:这是投机取巧的方式,正式开发中,尽量少用
var codeStr = 'parseInt(this.n1) ' + this.opt + ' parseInt(this.n2)'
this.result = eval(codeStr)
}
}
});
script>
body>
html>
这是一个邪恶的H1
这是一个邪恶的H1
这是一个邪恶的H1
这是一个邪恶的H1
具体例子:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<style>
.red {
color: red;
}
.thin {
font-weight: 200;
}
.italic {
font-style: italic;
}
.active {
letter-spacing: 0.5em;
}
style>
head>
<body>
<div id="app">
<h1 :class="classObj">这是一个很大很大的H1,大到你无法想象!!!h1>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: true,
classObj: { red: true, thin: true, italic: false, active: false }
},
methods: {}
});
script>
body>
html>
:style
的形式,书写样式对象这是一个善良的H1
data
中,并直接引用到 :style
中data: {
h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' }
}
这是一个善良的H1
:style
中通过数组,引用多个 data
上的样式对象data: {
h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' },
h1StyleObj2: { fontStyle: 'italic' }
}
这是一个善良的H1
具体例子:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<h1 :style="[ styleObj1, styleObj2 ]">这是一个h1h1>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
styleObj1: { color: 'red', 'font-weight': 200 },
styleObj2: { 'font-style': 'italic' }
},
methods: {}
});
script>
body>
html>
v-for
和key
属性
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<p v-for="(item, i) in list">索引值:{{i}} --- 每一项:{{item}}p>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
list: [1, 2, 3, 4, 5, 6]
},
methods: {}
});
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<p v-for="(user, i) in list">Id:{{ user.id }} --- 名字:{{ user.name }} --- 索引:{{i}}p>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
list: [
{ id: 1, name: 'zs1' },
{ id: 2, name: 'zs2' },
{ id: 3, name: 'zs3' },
{ id: 4, name: 'zs4' }
]
},
methods: {}
});
script>
body>
html>
2.迭代对象中的属性
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<p v-for="(val, key, i) in user">值是: {{ val }} --- 键是: {{key}} -- 索引: {{i}}p>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
user: {
id: 1,
name: '托尼·屎大颗',
gender: '男'
}
},
methods: {}
});
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<p v-for="count in 10">这是第 {{ count }} 次循环p>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {}
});
script>
body>
html>
2.2.0+ 的版本里,当在组件中使用 v-for 时,key 现在是必须的。
当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用 “就地复用” 策略。如果数据项的顺序被改变,Vue将不是移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。
v-if
和v-show
一般来说,v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此,如果需要频繁切换 v-show 较好,如果在运行时条件不大可能改变 v-if 较好。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<input type="button" value="toggle" @click="flag=!flag">
<h3 v-if="flag">这是用v-if控制的元素h3>
<h3 v-show="flag">这是用v-show控制的元素h3>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: false
},
methods: {
/* toggle() {
this.flag = !this.flag
} */
}
});
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<link rel="stylesheet" href="./lib/bootstrap-3.3.7.css">
head>
<body>
<div id="app">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">添加品牌h3>
div>
<div class="panel-body form-inline">
<label>
Id:
<input type="text" class="form-control" v-model="id">
label>
<label>
Name:
<input type="text" class="form-control" v-model="name">
label>
<input type="button" value="添加" class="btn btn-primary" @click="add()">
<label>
搜索名称关键字:
<input type="text" class="form-control" v-model="keywords">
label>
div>
div>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Idth>
<th>Nameth>
<th>Ctimeth>
<th>Operationth>
tr>
thead>
<tbody>
<tr v-for="item in search(keywords)" :key="item.id">
<td>{{ item.id }}td>
<td v-text="item.name">td>
<td>{{ item.ctime }}td>
<td>
<a href="" @click.prevent="del(item.id)">删除a>
td>
tr>
tbody>
table>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
id: '',
name: '',
keywords: '', // 搜索的关键字
list: [
{ id: 1, name: '奔驰', ctime: new Date() },
{ id: 2, name: '宝马', ctime: new Date() }
]
},
methods: {
add() { // 添加的方法
// console.log('ok')
// 分析:
// 1. 获取到 id 和 name ,直接从 data 上面获取
// 2. 组织出一个对象
// 3. 把这个对象,调用 数组的 相关方法,添加到 当前 data 上的 list 中
// 4. 注意:在Vue中,已经实现了数据的双向绑定,每当我们修改了 data 中的数据,Vue会默认监听到数据的改动,自动把最新的数据,应用到页面上;
// 5. 当我们意识到上面的第四步的时候,就证明大家已经入门Vue了,我们更多的是在进行 VM中 Model 数据的操作,同时,在操作Model数据的时候,指定的业务逻辑操作;
var car = { id: this.id, name: this.name, ctime: new Date() }
this.list.push(car)
this.id = this.name = ''
},
del(id) { // 根据Id删除数据
// 分析:
// 1. 如何根据Id,找到要删除这一项的索引
// 2. 如果找到索引了,直接调用 数组的 splice 方法
/* this.list.some((item, i) => {
if (item.id == id) {
this.list.splice(i, 1)
// 在 数组的 some 方法中,如果 return true,就会立即终止这个数组的后续循环
return true;
}
}) */
var index = this.list.findIndex(item => {
if (item.id == id) {
return true;
}
})
// console.log(index)
this.list.splice(index, 1)
},
search(keywords) { // 根据关键字,进行数据的搜索
/* var newList = []
this.list.forEach(item => {
if (item.name.indexOf(keywords) != -1) {
newList.push(item)
}
})
return newList */
// 注意: forEach some filter findIndex 这些都属于数组的新方法,
// 都会对数组中的每一项,进行遍历,执行相关的操作;
return this.list.filter(item => {
// if(item.name.indexOf(keywords) != -1)
// 注意 : ES6中,为字符串提供了一个新方法,叫做 String.prototype.includes('要包含的字符串')
// 如果包含,则返回 true ,否则返回 false
// contain
if (item.name.includes(keywords)) {
return item
}
})
// return newList
}
}
});
script>
body>
html>
filterBy - 指令
<tr v-for="item in list | filterBy searchName in 'name'">
<td>{{item.id}}td>
<td>{{item.name}}td>
<td>{{item.ctime}}td>
<td>
<a href="#" @click.prevent="del(item.id)">删除a>
td>
tr>
searchName
属性:<hr> 输入筛选名称:
<input type="text" v-model="searchName">
v-for
指令循环每一行数据的时候,不再直接 item in list
,而是 in
一个 过滤的methods 方法,同时,把过滤条件searchName
传递进去:<tbody>
<tr v-for="item in search(searchName)">
<td>{{item.id}}td>
<td>{{item.name}}td>
<td>{{item.ctime}}td>
<td>
<a href="#" @click.prevent="del(item.id)">删除a>
td>
tr>
tbody>
search
过滤方法中,使用 数组的 filter
方法进行过滤:search(name) {
return this.list.filter(x => {
return x.name.indexOf(name) != -1;
});
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<link rel="stylesheet" href="./lib/bootstrap-3.3.7.css">
head>
<body>
<div id="app">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">添加品牌h3>
div>
<div class="panel-body form-inline">
<label>
Id:
<input type="text" class="form-control" v-model="id">
label>
<label>
Name:
<input type="text" class="form-control" v-model="name" @keyup.f2="add">
label>
<input type="button" value="添加" class="btn btn-primary" @click="add()">
<label>
搜索名称关键字:
<input type="text" class="form-control" v-model="keywords" id="search" v-focus v-color="'green'">
label>
div>
div>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Idth>
<th>Nameth>
<th>Ctimeth>
<th>Operationth>
tr>
thead>
<tbody>
<tr v-for="item in search(keywords)" :key="item.id">
<td>{{ item.id }}td>
<td v-text="item.name">td>
<td>{{ item.ctime | dateFormat() }}td>
<td>
<a href="" @click.prevent="del(item.id)">删除a>
td>
tr>
tbody>
table>
div>
<div id="app2">
<h3 v-color="'pink'" v-fontweight="900" v-fontsize="50">{{ dt | dateFormat }}h3>
div>
<script>
// 全局的过滤器, 进行时间的格式化
// 所谓的全局过滤器,就是所有的VM实例都共享的
Vue.filter('dateFormat', function (dateStr, pattern = "") {
// 根据给定的时间字符串,得到特定的时间
var dt = new Date(dateStr)
// yyyy-mm-dd
var y = dt.getFullYear()
var m = dt.getMonth() + 1
var d = dt.getDate()
// return y + '-' + m + '-' + d
if (pattern.toLowerCase() === 'yyyy-mm-dd') {
return `${y}-${m}-${d}`
} else {
var hh = dt.getHours()
var mm = dt.getMinutes()
var ss = dt.getSeconds()
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}
})
// 自定义全局按键修饰符
Vue.config.keyCodes.f2 = 113
// 使用 Vue.directive() 定义全局的指令 v-focus
// 其中:参数1 : 指令的名称,注意,在定义的时候,指令的名称前面,不需要加 v- 前缀,
// 但是: 在调用的时候,必须 在指令名称前 加上 v- 前缀来进行调用
// 参数2: 是一个对象,这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作
Vue.directive('focus', {
bind: function (el) { // 每当指令绑定到元素上的时候,会立即执行这个 bind 函数,只执行一次
// 注意: 在每个 函数中,第一个参数,永远是 el ,表示 被绑定了指令的那个元素,这个 el 参数,是一个原生的JS对象
// 在元素 刚绑定了指令的时候,还没有 插入到 DOM中去,这时候,调用 focus 方法没有作用
// 因为,一个元素,只有插入DOM之后,才能获取焦点
// el.focus()
},
inserted: function (el) { // inserted 表示元素 插入到DOM中的时候,会执行 inserted 函数【触发1次】
el.focus()
// 和JS行为有关的操作,最好在 inserted 中去执行,放置 JS行为不生效
},
updated: function (el) { // 当VNode更新的时候,会执行 updated, 可能会触发多次
}
})
// 自定义一个 设置字体颜色的 指令
Vue.directive('color', {
// 样式,只要通过指令绑定给了元素,不管这个元素有没有被插入到页面中去,这个元素肯定有了一个内联的样式
// 将来元素肯定会显示到页面中,这时候,浏览器的渲染引擎必然会解析样式,应用给这个元素
bind: function (el, binding) {
// el.style.color = 'red'
// console.log(binding.name)
// 和样式相关的操作,一般都可以在 bind 执行
// console.log(binding.value)
// console.log(binding.expression)
el.style.color = binding.value
}
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
id: '',
name: '',
keywords: '', // 搜索的关键字
list: [
{ id: 1, name: '奔驰', ctime: new Date() },
{ id: 2, name: '宝马', ctime: new Date() }
]
},
methods: {
add() { // 添加的方法
// console.log('ok')
// 分析:
// 1. 获取到 id 和 name ,直接从 data 上面获取
// 2. 组织出一个对象
// 3. 把这个对象,调用 数组的 相关方法,添加到 当前 data 上的 list 中
// 4. 注意:在Vue中,已经实现了数据的双向绑定,每当我们修改了 data 中的数据,Vue会默认监听到数据的改动,自动把最新的数据,应用到页面上;
// 5. 当我们意识到上面的第四步的时候,就证明大家已经入门Vue了,我们更多的是在进行 VM中 Model 数据的操作,同时,在操作Model数据的时候,指定的业务逻辑操作;
var car = { id: this.id, name: this.name, ctime: new Date() }
this.list.push(car)
this.id = this.name = ''
},
del(id) { // 根据Id删除数据
// 分析:
// 1. 如何根据Id,找到要删除这一项的索引
// 2. 如果找到索引了,直接调用 数组的 splice 方法
/* this.list.some((item, i) => {
if (item.id == id) {
this.list.splice(i, 1)
// 在 数组的 some 方法中,如果 return true,就会立即终止这个数组的后续循环
return true;
}
}) */
var index = this.list.findIndex(item => {
if (item.id == id) {
return true;
}
})
// console.log(index)
this.list.splice(index, 1)
},
search(keywords) { // 根据关键字,进行数据的搜索
/* var newList = []
this.list.forEach(item => {
if (item.name.indexOf(keywords) != -1) {
newList.push(item)
}
})
return newList */
// 注意: forEach some filter findIndex 这些都属于数组的新方法,
// 都会对数组中的每一项,进行遍历,执行相关的操作;
return this.list.filter(item => {
// if(item.name.indexOf(keywords) != -1)
// 注意 : ES6中,为字符串提供了一个新方法,叫做 String.prototype.includes('要包含的字符串')
// 如果包含,则返回 true ,否则返回 false
// contain
if (item.name.includes(keywords)) {
return item
}
})
// return newList
}
}
});
// 如何自定义一个私有的过滤器(局部)
var vm2 = new Vue({
el: '#app2',
data: {
dt: new Date()
},
methods: {},
filters: { // 定义私有过滤器 过滤器有两个 条件 【过滤器名称 和 处理函数】
// 过滤器调用的时候,采用的是就近原则,如果私有过滤器和全局过滤器名称一致了,这时候 优先调用私有过滤器
dateFormat: function (dateStr, pattern = '') {
// 根据给定的时间字符串,得到特定的时间
var dt = new Date(dateStr)
// yyyy-mm-dd
var y = dt.getFullYear()
var m = (dt.getMonth() + 1).toString().padStart(2, '0')
var d = dt.getDate().toString().padStart(2, '0')
if (pattern.toLowerCase() === 'yyyy-mm-dd') {
return `${y}-${m}-${d}`
} else {
var hh = dt.getHours().toString().padStart(2, '0')
var mm = dt.getMinutes().toString().padStart(2, '0')
var ss = dt.getSeconds().toString().padStart(2, '0')
return `${y}-${m}-${d} ${hh}:${mm}:${ss} ~~~~~~~`
}
}
},
directives: { // 自定义私有指令
'fontweight': { // 设置字体粗细的
bind: function (el, binding) {
el.style.fontWeight = binding.value
}
},
'fontsize': function (el, binding) { // 注意:这个 function 等同于 把 代码写到了 bind 和 update 中去
el.style.fontSize = parseInt(binding.value) + 'px'
}
}
})
// 过滤器的定义语法
// Vue.filter('过滤器的名称', function(){})
// 过滤器中的 function ,第一个参数,已经被规定死了,永远都是 过滤器 管道符前面 传递过来的数据
/* Vue.filter('过滤器的名称', function (data) {
return data + '123'
}) */
// document.getElementById('search').focus()
script>
body>
html>
vue-devtools
的安装步骤和使用Vue.js devtools - 安装方式 - 推荐
概念:Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示;
<td>{{item.ctime | dataFormat('yyyy-mm-dd')}}td>
filters
定义方式:
filters: { // 私有局部过滤器,只能在 当前 VM 对象所控制的 View 区域进行使用
dataFormat(input, pattern = "") { // 在参数列表中 通过 pattern="" 来指定形参默认值,防止报错
var dt = new Date(input);
// 获取年月日
var y = dt.getFullYear();
var m = (dt.getMonth() + 1).toString().padStart(2, '0');
var d = dt.getDate().toString().padStart(2, '0');
// 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日
// 否则,就返回 年-月-日 时:分:秒
if (pattern.toLowerCase() === 'yyyy-mm-dd') {
return `${y}-${m}-${d}`;
} else {
// 获取时分秒
var hh = dt.getHours().toString().padStart(2, '0');
var mm = dt.getMinutes().toString().padStart(2, '0');
var ss = dt.getSeconds().toString().padStart(2, '0');
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
}
}
}
使用ES6中的字符串新方法 String.prototype.padStart(maxLength, fillString=’’) 或 String.prototype.padEnd(maxLength, fillString=’’)来填充字符串;
// 定义一个全局过滤器
Vue.filter('dataFormat', function (input, pattern = '') {
var dt = new Date(input);
// 获取年月日
var y = dt.getFullYear();
var m = (dt.getMonth() + 1).toString().padStart(2, '0');
var d = dt.getDate().toString().padStart(2, '0');
// 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日
// 否则,就返回 年-月-日 时:分:秒
if (pattern.toLowerCase() === 'yyyy-mm-dd') {
return `${y}-${m}-${d}`;
} else {
// 获取时分秒
var hh = dt.getHours().toString().padStart(2, '0');
var mm = dt.getMinutes().toString().padStart(2, '0');
var ss = dt.getSeconds().toString().padStart(2, '0');
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
}
});
注意:当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用!
Vue.directive('on').keyCodes.f2 = 113;
Vue.config.keyCodes.名称 = 按键值
来自定义案件修饰符的别名:Vue.config.keyCodes.f2 = 113;
// 自定义全局指令 v-focus,为绑定的元素自动获取焦点:
Vue.directive('focus', {
inserted: function (el) { // inserted 表示被绑定元素插入父节点时调用
el.focus();
}
});
// 自定义局部指令 v-color 和 v-font-weight,为绑定的元素设置指定的字体颜色 和 字体粗细:
directives: {
color: { // 为元素设置指定的字体颜色
bind(el, binding) {
el.style.color = binding.value;
}
},
'font-weight': function (el, binding2) { // 自定义指令的简写形式,等同于定义了 bind 和 update 两个钩子函数
el.style.fontWeight = binding2.value;
}
}
<input type="text" v-model="searchName" v-focus v-color="'red'" v-font-weight="900">
Vue.elementDirective('red-color', {
bind: function () {
this.el.style.color = 'red';
}
});
使用方式:
<red-color>1232red-color>
创建期间的生命周期函数:
运行期间的生命周期函数:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<input type="button" value="修改msg" @click="msg='No'">
<h3 id="h3">{{ msg }}h3>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
msg: 'ok'
},
methods: {
show() {
console.log('执行了show方法')
}
},
beforeCreate() { // 这是我们遇到的第一个生命周期函数,表示实例完全被创建出来之前,会执行它
// console.log(this.msg)
// this.show()
// 注意: 在 beforeCreate 生命周期函数执行的时候,data 和 methods 中的 数据都还没有没初始化
},
created() { // 这是遇到的第二个生命周期函数
// console.log(this.msg)
// this.show()
// 在 created 中,data 和 methods 都已经被初始化好了!
// 如果要调用 methods 中的方法,或者操作 data 中的数据,最早,只能在 created 中操作
},
beforeMount() { // 这是遇到的第3个生命周期函数,表示 模板已经在内存中编辑完成了,但是尚未把 模板渲染到 页面中
// console.log(document.getElementById('h3').innerText)
// 在 beforeMount 执行的时候,页面中的元素,还没有被真正替换过来,只是之前写的一些模板字符串
},
mounted() { // 这是遇到的第4个生命周期函数,表示,内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了
// console.log(document.getElementById('h3').innerText)
// 注意: mounted 是 实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了,此时,如果没有其它操作的话,这个实例,就静静的 躺在我们的内存中,一动不动
},
// 接下来的是运行中的两个事件
beforeUpdate() { // 这时候,表示 我们的界面还没有被更新【数据被更新了吗? 数据肯定被更新了】
/* console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
console.log('data 中的 msg 数据是:' + this.msg) */
// 得出结论: 当执行 beforeUpdate 的时候,页面中的显示的数据,还是旧的,此时 data 数据是最新的,页面尚未和 最新的数据保持同步
},
updated() {
console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
console.log('data 中的 msg 数据是:' + this.msg)
// updated 事件执行的时候,页面和 data 数据已经保持同步了,都是最新的
}
});
script>
body>
html>
除了 vue-resource 之外,还可以使用 axios
的第三方包实现实现数据的请求
// 导入 http 内置模块
const http = require('http')
// 这个核心模块,能够帮我们解析 URL地址,从而拿到 pathname query
const urlModule = require('url')
// 创建一个 http 服务器
const server = http.createServer()
// 监听 http 服务器的 request 请求
server.on('request', function (req, res) {
// const url = req.url
const { pathname: url, query } = urlModule.parse(req.url, true)
if (url === '/getscript') {
// 拼接一个合法的JS脚本,这里拼接的是一个方法的调用
// var scriptStr = 'show()'
var data = {
name: 'xjj',
age: 18,
gender: '女孩子'
}
var scriptStr = `${query.callback}(${JSON.stringify(data)})`
// res.end 发送给 客户端, 客户端去把 这个 字符串,当作JS代码去解析执行
res.end(scriptStr)
} else {
res.end('404')
}
})
// 指定端口号并启动服务器监听
server.listen(3000, function () {
console.log('server listen at http://127.0.0.1:3000')
})
script
标签,引入 vue-resource
的脚本文件;Vue
的脚本文件,再引用 vue-resource
的脚本文件;<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
<!-- 注意:vue-resource 依赖于 Vue,所以先后顺序要注意 -->
<!-- this.$http.jsonp -->
<script src="./lib/vue-resource-1.3.4.js"></script>
</head>
<body>
<div id="app">
<input type="button" value="get请求" @click="getInfo">
<input type="button" value="post请求" @click="postInfo">
<input type="button" value="jsonp请求" @click="jsonpInfo">
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {
getInfo() { // 发起get请求
// 当发起get请求之后, 通过 .then 来设置成功的回调函数
this.$http.get('http://vue.studyit.io/api/getlunbo').then(function (result) {
// 通过 result.body 拿到服务器返回的成功的数据
// console.log(result.body)
})
},
postInfo() { // 发起 post 请求 application/x-wwww-form-urlencoded
// 手动发起的 Post 请求,默认没有表单格式,所以,有的服务器处理不了
// 通过 post 方法的第三个参数, { emulateJSON: true } 设置 提交的内容类型 为 普通表单数据格式
this.$http.post('http://vue.studyit.io/api/post', {}, { emulateJSON: true }).then(result => {
console.log(result.body)
})
},
jsonpInfo() { // 发起JSONP 请求
this.$http.jsonp('http://vue.studyit.io/api/jsonp').then(result => {
console.log(result.body)
})
}
}
});
</script>
</body>
</html>
客户端JSONP页面
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
head>
<body>
<script>
function showInfo123(data) {
console.log(data)
}
script>
<script src="http://127.0.0.1:3000/getscript?callback=showInfo123">script>
body>
html>
PHPStudy
;Navicat
这个数据库可视化工具,并激活;Navicat
工具,新建空白数据库,名为 dtcmsdb4
;右键
-> 运行SQL文件
,选择并执行 dtcmsdb4.sql
这个数据库脚本文件;如果执行不报错,则数据库导入完成;vuecms3_nodejsapi
内部,执行 npm i
安装所有的依赖项;nodemon
, 没有安装,则运行 npm i nodemon -g
进行全局安装,安装完毕后,进入到 vuecms3_nodejsapi
目录 -> src
目录 -> 双击运行 start.bat
app.js
中第 14行
中数据库连接配置字符串是否正确;PHPStudy 中默认的 用户名是root,默认的密码也是root为什么要有动画:动画能够提高用户的体验,帮助用户更好的理解页面中的功能;
不使用动画
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<input type="button" value="toggle" @click="flag=!flag">
<h3 v-if="flag">这是一个H3h3>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: false
},
methods: {}
});
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<style>
/* v-enter 【这是一个时间点】 是进入之前,元素的起始状态,此时还没有开始进入 */
/* v-leave-to 【这是一个时间点】 是动画离开之后,离开的终止状态,此时,元素 动画已经结束了 */
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(150px);
}
/* v-enter-active 【入场动画的时间段】 */
/* v-leave-active 【离场动画的时间段】 */
.v-enter-active,
.v-leave-active{
transition: all 0.8s ease;
}
style>
head>
<body>
<div id="app">
<input type="button" value="toggle" @click="flag=!flag">
<transition>
<h3 v-if="flag">这是一个H3h3>
transition>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: false
},
methods: {}
});
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<style>
/* v-enter 【这是一个时间点】 是进入之前,元素的起始状态,此时还没有开始进入 */
/* v-leave-to 【这是一个时间点】 是动画离开之后,离开的终止状态,此时,元素 动画已经结束了 */
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(150px);
}
/* v-enter-active 【入场动画的时间段】 */
/* v-leave-active 【离场动画的时间段】 */
.v-enter-active,
.v-leave-active{
transition: all 0.8s ease;
}
.my-enter,
.my-leave-to {
opacity: 0;
transform: translateY(70px);
}
.my-enter-active,
.my-leave-active{
transition: all 0.8s ease;
}
style>
head>
<body>
<div id="app">
<input type="button" value="toggle" @click="flag=!flag">
<transition>
<h3 v-if="flag">这是一个H3h3>
transition>
<hr>
<input type="button" value="toggle2" @click="flag2=!flag2">
<transition name="my">
<h6 v-if="flag2">这是一个H6h6>
transition>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: false,
flag2: false
},
methods: {}
});
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<link rel="stylesheet" href="./lib/animate.css">
head>
<body>
<div id="app">
<input type="button" value="toggle" @click="flag=!flag">
<transition
enter-active-class="bounceIn"
leave-active-class="bounceOut"
:duration="{ enter: 200, leave: 400 }">
<h3 v-if="flag" class="animated">这是一个H3h3>
transition>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: false
},
methods: {}
});
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<style>
.ball {
width: 15px;
height: 15px;
border-radius: 50%;
background-color: red;
}
style>
head>
<body>
<div id="app">
<input type="button" value="快到碗里来" @click="flag=!flag">
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter">
<div class="ball" v-show="flag">div>
transition>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: false
},
methods: {
// 注意: 动画钩子函数的第一个参数:el,表示 要执行动画的那个DOM元素,是个原生的 JS DOM对象
// 大家可以认为 , el 是通过 document.getElementById('') 方式获取到的原生JS DOM对象
beforeEnter(el){
// beforeEnter 表示动画入场之前,此时,动画尚未开始,可以 在 beforeEnter 中,设置元素开始动画之前的起始样式
// 设置小球开始动画之前的,起始位置
el.style.transform = "translate(0, 0)"
},
enter(el, done){
// 这句话,没有实际的作用,但是,如果不写,出不来动画效果;
// 可以认为 el.offsetWidth 会强制动画刷新
el.offsetWidth
// enter 表示动画 开始之后的样式,这里,可以设置小球完成动画之后的,结束状态
el.style.transform = "translate(150px, 450px)"
el.style.transition = 'all 1s ease'
// 这里的 done, 起始就是 afterEnter 这个函数,也就是说:done 是 afterEnter 函数的引用
done()
},
afterEnter(el){
// 动画完成之后,会调用 afterEnter
// console.log('ok')
this.flag = !this.flag
}
}
});
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<style>
li {
border: 1px dashed #999;
margin: 5px;
line-height: 35px;
padding-left: 5px;
font-size: 12px;
width: 100%;
}
li:hover {
background-color: hotpink;
transition: all 0.8s ease;
}
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateY(80px);
}
.v-enter-active,
.v-leave-active {
transition: all 0.6s ease;
}
/* 下面的 .v-move 和 .v-leave-active 配合使用,能够实现列表后续的元素,渐渐地漂上来的效果 */
.v-move {
transition: all 0.6s ease;
}
.v-leave-active{
position: absolute;
}
style>
head>
<body>
<div id="app">
<div>
<label>
Id:
<input type="text" v-model="id">
label>
<label>
Name:
<input type="text" v-model="name">
label>
<input type="button" value="添加" @click="add">
div>
<transition-group appear tag="ul">
<li v-for="(item, i) in list" :key="item.id" @click="del(i)">
{{item.id}} --- {{item.name}}
li>
transition-group>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
id: '',
name: '',
list: [
{ id: 1, name: '赵高' },
{ id: 2, name: '秦桧' },
{ id: 3, name: '严嵩' },
{ id: 4, name: '魏忠贤' }
]
},
methods: {
add() {
this.list.push({ id: this.id, name: this.name })
this.id = this.name = ''
},
del(i) {
this.list.splice(i, 1)
}
}
});
script>
body>
html>
组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的 v-move
特性,它会在元素的改变定位的过程中应用。
v-move
和 v-leave-active
结合使用,能够让列表的过渡更加平缓柔和:.v-move{
transition: all 0.8s ease;
}
.v-leave-active{
position: absolute;
}
什么是组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可;
组件化和模块化的不同:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<mycom1>mycom1>
div>
<script>
// 1.1 使用 Vue.extend 来创建全局的Vue组件
// var com1 = Vue.extend({
// template: '这是使用 Vue.extend 创建的组件
' // 通过 template 属性,指定了组件要展示的HTML结构
// })
// 1.2 使用 Vue.component('组件的名称', 创建出来的组件模板对象)
// Vue.component('myCom1', com1)
// 如果使用 Vue.component 定义全局组件的时候,组件名称使用了 驼峰命名,则在引用组件的时候,需要把 大写的驼峰改为小写的字母,同时,两个单词之前,使用 - 链接;
// 如果不使用驼峰,则直接拿名称来使用即可;
// Vue.component('mycom1', com1)
// Vue.component 第一个参数:组件的名称,将来在引用组件的时候,就是一个 标签形式 来引入 它的
// 第二个参数: Vue.extend 创建的组件 ,其中 template 就是组件将来要展示的HTML内容
Vue.component('mycom1', Vue.extend({
template: '这是使用 Vue.extend 创建的组件
'
}))
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {}
});
script>
body>
html>
2.直接使用 Vue.component 方法:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<mycom2>mycom2>
div>
<script>
// 注意:不论是哪种方式创建出来的组件,组件的 template 属性指向的模板内容,必须有且只能有唯一的一个根元素
Vue.component('mycom2', {
template: '这是直接使用 Vue.component 创建出来的组件
123'
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {}
});
script>
body>
html>
3.将模板字符串,定义到script标签种:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<mycom3>mycom3>
div>
<div id="app2">
<mycom3>mycom3>
<login>login>
div>
<template id="tmpl">
<div>
<h1>这是通过 template 元素,在外部定义的组件结构,这个方式,有代码的只能提示和高亮h1>
<h4>好用,不错!h4>
div>
template>
<template id="tmpl2">
<h1>这是私有的 login 组件h1>
template>
<script>
Vue.component('mycom3', {
template: '#tmpl'
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {}
});
var vm2 = new Vue({
el: '#app2',
data: {},
methods: {},
filters: {},
directives: {},
components: { // 定义实例内部私有组件的
login: {
template: '#tmpl2'
}
},
beforeCreate() { },
created() { },
beforeMount() { },
mounted() { },
beforeUpdate() { },
updated() { },
beforeDestroy() { },
destroyed() { }
})
script>
body>
html>
注意: 组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹!
在组件中,data
需要被定义为一个方法
在子组件中,如果将模板字符串,定义到了script标签中,那么,要访问子组件身上的data
属性中的值,需要使用this
来访问;
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<mycom1>mycom1>
div>
<script>
// 1. 组件可以有自己的 data 数据
// 2. 组件的 data 和 实例的 data 有点不一样,实例中的 data 可以为一个对象,但是 组件中的 data 必须是一个方法
// 3. 组件中的 data 除了必须为一个方法之外,这个方法内部,还必须返回一个对象才行;
// 4. 组件中 的data 数据,使用方式,和实例中的 data 使用方式完全一样!!!
Vue.component('mycom1', {
template: '这是全局组件 --- {{msg}}
',
data: function () {
return {
msg: '这是组件的中data定义的数据'
}
}
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {}
});
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<counter>counter>
<hr>
<counter>counter>
<hr>
<counter>counter>
div>
<template id="tmpl">
<div>
<input type="button" value="+1" @click="increment">
<h3>{{count}}h3>
div>
template>
<script>
var dataObj = { count: 0 }
// 这是一个计数器的组件, 身上有个按钮,每当点击按钮,让 data 中的 count 值 +1
Vue.component('counter', {
template: '#tmpl',
data: function () {
// return dataObj
return { count: 0 }
},
methods: {
increment() {
this.count++
}
}
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {}
});
script>
body>
html>
components
属性定义局部子组件组件实例定义方式:
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
components: { // 定义子组件
account: { // account 组件
template: '这是Account组件{{name}}
', // 在这里使用定义的子组件
components: { // 定义子组件的子组件
login: { // login 组件
template: "这是登录组件
"
}
}
}
}
});
script>
引用组件:
<div id="app">
<account></account>
</div>
flag
标识符结合v-if
和v-else
切换组件
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<a href="" @click.prevent="flag=true">登录a>
<a href="" @click.prevent="flag=false">注册a>
<login v-if="flag">login>
<register v-else="flag">register>
div>
<script>
Vue.component('login', {
template: '登录组件
'
})
Vue.component('register', {
template: '注册组件
'
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: false
},
methods: {}
});
script>
body>
html>
:is
属性来切换不同的子组件,并添加切换动画
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<style>
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(150px);
}
.v-enter-active,
.v-leave-active {
transition: all 0.5s ease;
}
style>
head>
<body>
<div id="app">
<a href="" @click.prevent="comName='login'">登录a>
<a href="" @click.prevent="comName='register'">注册a>
<transition mode="out-in">
<component :is="comName">component>
transition>
div>
<script>
// 组件名称是 字符串
Vue.component('login', {
template: '登录组件
'
})
Vue.component('register', {
template: '注册组件
'
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
},
methods: {}
});
script>
body>
html>
组件实例定义方式,注意:一定要使用props
属性来定义父组件传递过来的数据
使用v-bind
或简化指令,将数据传递到子组件中:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<com1 v-bind:parentmsg="msg">com1>
div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
msg: '123 啊-父组件中的数据'
},
methods: {},
components: {
// 结论:经过演示,发现,子组件中,默认无法访问到 父组件中的 data 上的数据 和 methods 中的方法
com1: {
data() { // 注意: 子组件中的 data 数据,并不是通过 父组件传递过来的,而是子组件自身私有的,比如: 子组件通过 Ajax ,请求回来的数据,都可以放到 data 身上;
// data 上的数据,都是可读可写的;
return {
title: '123',
content: 'qqq'
}
},
template: '这是子组件 --- {{ parentmsg }}
',
// 注意: 组件中的 所有 props 中的数据,都是通过 父组件传递给子组件的
// props 中的数据,都是只读的,无法重新赋值
props: ['parentmsg'], // 把父组件传递过来的 parentmsg 属性,先在 props 数组中,定义一下,这样,才能使用这个数据
directives: {},
filters: {},
components: {},
methods: {
change() {
this.parentmsg = '被修改了'
}
}
}
}
});
script>
body>
html>
getMsg
是父组件中methods
中定义的方法名称,func
是子组件调用传递过来方法时候的方法名称3.子组件内部通过this.$emit('方法名', 要传递的数据)
方式,来调用父组件中的方法,同时把数据传递给父组件使用
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<com2 @func="show">com2>
div>
<template id="tmpl">
<div>
<h1>这是 子组件h1>
<input type="button" value="这是子组件中的按钮 - 点击它,触发 父组件传递过来的 func 方法" @click="myclick">
div>
template>
<script>
// 定义了一个字面量类型的 组件模板对象
var com2 = {
template: '#tmpl', // 通过指定了一个 Id, 表示 说,要去加载 这个指定Id的 template 元素中的内容,当作 组件的HTML结构
data() {
return {
sonmsg: { name: '小头儿子', age: 6 }
}
},
methods: {
myclick() {
// 当点击子组件的按钮的时候,如何 拿到 父组件传递过来的 func 方法,并调用这个方法???
// emit 英文原意: 是触发,调用、发射的意思
// this.$emit('func123', 123, 456)
this.$emit('func', this.sonmsg)
}
}
}
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
datamsgFormSon: null
},
methods: {
show(data) {
// console.log('调用了父组件身上的 show 方法: --- ' + data)
// console.log(data);
this.datamsgFormSon = data
}
},
components: {
com2
// com2: com2
}
});
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<link rel="stylesheet" href="./lib/bootstrap-3.3.7.css">
head>
<body>
<div id="app">
<cmt-box @func="loadComments">cmt-box>
<ul class="list-group">
<li class="list-group-item" v-for="item in list" :key="item.id">
<span class="badge">评论人: {{ item.user }}span>
{{ item.content }}
li>
ul>
div>
<template id="tmpl">
<div>
<div class="form-group">
<label>评论人:label>
<input type="text" class="form-control" v-model="user">
div>
<div class="form-group">
<label>评论内容:label>
<textarea class="form-control" v-model="content">textarea>
div>
<div class="form-group">
<input type="button" value="发表评论" class="btn btn-primary" @click="postComment">
div>
div>
template>
<script>
var commentBox = {
data() {
return {
user: '',
content: ''
}
},
template: '#tmpl',
methods: {
postComment() { // 发表评论的方法
// 分析:发表评论的业务逻辑
// 1. 评论数据存到哪里去??? 存放到了 localStorage 中 localStorage.setItem('cmts', '')
// 2. 先组织出一个最新的评论数据对象
// 3. 想办法,把 第二步中,得到的评论对象,保存到 localStorage 中:
// 3.1 localStorage 只支持存放字符串数据, 要先调用 JSON.stringify
// 3.2 在保存 最新的 评论数据之前,要先从 localStorage 获取到之前的评论数据(string), 转换为 一个 数组对象, 然后,把最新的评论, push 到这个数组
// 3.3 如果获取到的 localStorage 中的 评论字符串,为空不存在, 则 可以 返回一个 '[]' 让 JSON.parse 去转换
// 3.4 把 最新的 评论列表数组,再次调用 JSON.stringify 转为 数组字符串,然后调用 localStorage.setItem()
var comment = { id: Date.now(), user: this.user, content: this.content }
// 从 localStorage 中获取所有的评论
var list = JSON.parse(localStorage.getItem('cmts') || '[]')
list.unshift(comment)
// 重新保存最新的 评论数据
localStorage.setItem('cmts', JSON.stringify(list))
this.user = this.content = ''
// this.loadComments() // ?????
this.$emit('func')
}
}
}
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
list: [
{ id: Date.now(), user: '李白', content: '天生我材必有用' },
{ id: Date.now(), user: '江小白', content: '劝君更尽一杯酒' },
{ id: Date.now(), user: '小马', content: '我姓马, 风吹草低见牛羊的马' }
]
},
beforeCreate(){ // 注意:这里不能调用 loadComments 方法,因为在执行这个钩子函数的时候,data 和 methods 都还没有被初始化好
},
created(){
this.loadComments()
},
methods: {
loadComments() { // 从本地的 localStorage 中,加载评论列表
var list = JSON.parse(localStorage.getItem('cmts') || '[]')
this.list = list
}
},
components: {
'cmt-box': commentBox
}
});
script>
body>
html>
使用 this.$refs
来获取元素和组件
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
head>
<body>
<div id="app">
<input type="button" value="获取元素" @click="getElement" ref="mybtn">
<h3 id="myh3" ref="myh3">哈哈哈, 今天天气太好了!!!h3>
<hr>
<login ref="mylogin">login>
div>
<script>
var login = {
template: '登录组件
',
data() {
return {
msg: 'son msg'
}
},
methods: {
show() {
console.log('调用了子组件的方法')
}
}
}
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {
getElement() {
// console.log(document.getElementById('myh3').innerText)
// ref 是 英文单词 【reference】 值类型 和 引用类型 referenceError
// console.log(this.$refs.myh3.innerText)
// console.log(this.$refs.mylogin.msg)
// this.$refs.mylogin.show()
}
},
components: {
login
}
});
script>
body>
html>
对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源;
对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;
在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由);
Vue.extend
创建组件
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<script src="./lib/vue-router-3.0.1.js">script>
<style>
.router-link-active,
.myactive {
color: red;
font-weight: 800;
font-style: italic;
font-size: 80px;
text-decoration: underline;
background-color: green;
}
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(140px);
}
.v-enter-active,
.v-leave-active {
transition: all 0.5s ease;
}
style>
head>
<body>
<div id="app">
<router-link to="/login" tag="span">登录router-link>
<router-link to="/register">注册router-link>
<transition mode="out-in">
<router-view>router-view>
transition>
div>
<script>
// 组件的模板对象
var login = {
template: '登录组件
'
}
var register = {
template: '注册组件
'
}
/* Vue.component('login', {
template: '登录组件
'
}) */
// 2. 创建一个路由对象, 当 导入 vue-router 包之后,在 window 全局对象中,就有了一个 路由的构造函数,叫做 VueRouter
// 在 new 路由对象的时候,可以为 构造函数,传递一个配置对象
var routerObj = new VueRouter({
// route // 这个配置对象中的 route 表示 【路由匹配规则】 的意思
routes: [ // 路由匹配规则
// 每个路由规则,都是一个对象,这个规则对象,身上,有两个必须的属性:
// 属性1 是 path, 表示监听 哪个路由链接地址;
// 属性2 是 component, 表示,如果 路由是前面匹配到的 path ,则展示 component 属性对应的那个组件
// 注意: component 的属性值,必须是一个 组件的模板对象, 不能是 组件的引用名称;
// { path: '/', component: login },
{ path: '/', redirect: '/login' }, // 这里的 redirect 和 Node 中的 redirect 完全是两码事
{ path: '/login', component: login },
{ path: '/register', component: register }
],
linkActiveClass: 'myactive'
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router: routerObj // 将路由规则对象,注册到 vm 实例上,用来监听 URL 地址的变化,然后展示对应的组件
});
script>
body>
html>
在规则中定义参数:
通过 this.$route.params
来获取路由中的参数:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<script src="./lib/vue-router-3.0.1.js">script>
head>
<body>
<div id="app">
<router-link to="/login?id=10&name=zs">登录router-link>
<router-link to="/register">注册router-link>
<router-view>router-view>
div>
<script>
var login = {
template: '登录 --- {{ $route.query.id }} --- {{ $route.query.name }}
',
data(){
return {
msg: '123'
}
},
created(){ // 组件的生命周期钩子函数
// console.log(this.$route)
// console.log(this.$route.query.id)
}
}
var register = {
template: '注册
'
}
var router = new VueRouter({
routes: [
{ path: '/login', component: login },
{ path: '/register', component: register }
]
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
// router: router
router
});
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<script src="./lib/vue-router-3.0.1.js">script>
head>
<body>
<div id="app">
<router-link to="/login/12/ls">登录router-link>
<router-link to="/register">注册router-link>
<router-view>router-view>
div>
<script>
var login = {
template: '登录 --- {{ $route.params.id }} --- {{ $route.params.name }}
',
data(){
return {
msg: '123'
}
},
created(){ // 组件的生命周期钩子函数
console.log(this.$route.params.id)
}
}
var register = {
template: '注册
'
}
var router = new VueRouter({
routes: [
{ path: '/login/:id/:name', component: login },
{ path: '/register', component: register }
]
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
// router: router
router
});
script>
body>
html>
children
属性实现路由嵌套
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<script src="./lib/vue-router-3.0.1.js">script>
head>
<body>
<div id="app">
<router-link to="/account">Accountrouter-link>
<router-view>router-view>
div>
<template id="tmpl">
<div>
<h1>这是 Account 组件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: '#tmpl'
}
var login = {
template: '登录
'
}
var register = {
template: '注册
'
}
var router = new VueRouter({
routes: [
{
path: '/account',
component: account,
// 使用 children 属性,实现子路由,同时,子路由的 path 前面,不要带 / ,否则永远以根路径开始请求,这样不方便我们用户去理解URL地址
children: [
{ path: 'login', component: login },
{ path: 'register', component: register }
]
}
// { path: '/account/login', component: login },
// { path: '/account/register', component: register }
]
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router
});
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<script src="./lib/vue-router-3.0.1.js">script>
<style>
html,
body {
margin: 0;
padding: 0;
}
.header {
background-color: orange;
height: 80px;
}
h1 {
margin: 0;
padding: 0;
font-size: 16px;
}
.container {
display: flex;
height: 600px;
}
.left {
background-color: lightgreen;
flex: 2;
}
.main {
background-color: 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: 'Header头部区域
'
}
var leftBox = {
template: 'Left侧边栏区域
'
}
var mainBox = {
template: 'mainBox主体区域
'
}
// 创建路由对象
var router = new VueRouter({
routes: [
/* { path: '/', component: header },
{ path: '/left', component: leftBox },
{ path: '/main', component: mainBox } */
{
path: '/', components: {
'default': header,
'left': leftBox,
'main': mainBox
}
}
]
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router
});
script>
body>
html>
watch
属性的使用考虑一个问题:想要实现 名
和 姓
两个文本框的内容改变,则全名的文本框中的值也跟着改变;(用以前的知识如何实现???)
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./lib/vue-2.4.0.js">script>
<script src="./lib/vue-router-3.0.1.js">script>
head>
<body>
<div id="app">
<router-link to="/login">登录router-link>
<router-link to="/register">注册router-link>
<router-view>router-view>
div>
<script>
// 2. 创建子组件
var login = {
template: '这是登录子组件,这个组件是 奔波霸 开发的。
'
}
var register = {
template: '这是注册子组件,这个组件是 霸波奔 开发的。
'
}
// 3. 创建一个路由对象
var router = new VueRouter({
routes: [ // 路由规则数组
{ path: '/', redirect: '/login' },
{ path: '/login', component: login },
{ path: '/register', component: register }
],
linkActiveClass: 'myactive' // 和激活相关的类
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
// router: router
router,
watch: {
// this.$route.path
'$route.path': function (newVal, oldVal) {
// console.log(newVal + ' --- ' + oldVal)
if (newVal === '/login') {
console.log('欢迎进入登录页面')
} else if (newVal === '/register') {
console.log('欢迎进入注册页面')
}
}
}
});
script>
body>
html>
computed
计算属性的使用<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="firstname"> +
<input type="text" v-model="middlename"> +
<input type="text" v-model="lastname"> =
<input type="text" v-model="fullname">
<p>{{ fullname }}</p>
<p>{{ fullname }}</p>
<p>{{ fullname }}</p>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
firstname: '',
lastname: '',
middlename: ''
},
methods: {},
computed: { // 在 computed 中,可以定义一些 属性,这些属性,叫做 【计算属性】, 计算属性的,本质,就是 一个方法,只不过,我们在使用 这些计算属性的时候,是把 它们的 名称,直接当作 属性来使用的;并不会把 计算属性,当作方法去调用;
// 注意1: 计算属性,在引用的时候,一定不要加 () 去调用,直接把它 当作 普通 属性去使用就好了;
// 注意2: 只要 计算属性,这个 function 内部,所用到的 任何 data 中的数据发送了变化,就会 立即重新计算 这个 计算属性的值
// 注意3: 计算属性的求值结果,会被缓存起来,方便下次直接使用; 如果 计算属性方法中,所以来的任何数据,都没有发生过变化,则,不会重新对 计算属性求值;
'fullname': function () {
console.log('ok')
return this.firstname + '-' + this.middlename + '-' + this.lastname
}
}
});
</script>
</body>
</html>
watch
、computed
和methods
之间的对比computed
属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;methods
方法表示一个具体的操作,主要书写业务逻辑;watch
一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是computed
和methods
的结合体;nrm
的安装使用作用:提供了一些最常用的NPM包镜像地址,能够让我们快速的切换安装包时候的服务器地址;
什么是镜像:原来包刚一开始是只存在于国外的NPM服务器,但是由于网络原因,经常访问不到,这时候,我们可以在国内,创建一个和官网完全一样的NPM服务器,只不过,数据都是从人家那里拿过来的,除此之外,使用方式完全一样;
npm i nrm -g
全局安装nrm
包;nrm ls
查看当前所有可用的镜像源地址以及当前所使用的镜像源地址;nrm use npm
或nrm use taobao
切换不同的镜像源地址;webpack 是前端的一个项目构建工具,它是基于 Node.js 开发出来的一个前端工具;
npm i webpack -g
全局安装webpack,这样就能在全局使用webpack的命令npm i webpack --save-dev
安装到项目依赖中npm init
初始化项目,使用npm管理项目中的依赖包cnpm i jquery --save
安装jquery类库main.js
并书写各行变色的代码逻辑:// 导入jquery类库
import $ from 'jquery'
// 设置偶数行背景色,索引从0开始,0是偶数
$('#list li:even').css('backgroundColor','lightblue');
// 设置奇数行背景色
$('#list li:odd').css('backgroundColor','pink');
main.js
会报错,因为浏览器不认识import
这种高级的JS语法,需要使用webpack进行处理,webpack默认会把这种高级的语法转换为低级的浏览器能识别的语法;webpack 入口文件路径 输出文件路径
对main.js
进行处理:webpack src/js/main.js dist/bundle.js
webpack.config.js
webpack.config.js
中配置这两个路径: // 导入处理路径的模块
var path = require('path');
// 导出一个配置对象,将来webpack在启动的时候,会默认来查找webpack.config.js,并读取这个文件中导出的配置对象,来进行打包处理
module.exports = {
entry: path.resolve(__dirname, 'src/js/main.js'), // 项目入口文件
output: { // 配置输出选项
path: path.resolve(__dirname, 'dist'), // 配置输出的路径
filename: 'bundle.js' // 配置输出的文件名
}
}
webpack-dev-server
来实现代码实时打包编译,当修改代码之后,会自动进行打包构建。cnpm i webpack-dev-server --save-dev
安装到开发依赖webpack-dev-server
来进行打包,发现报错,此时需要借助于package.json
文件中的指令,来进行运行webpack-dev-server
命令,在scripts
节点下新增"dev": "webpack-dev-server"
指令,发现可以进行实时打包,但是dist目录下并没有生成bundle.js
文件,这是因为webpack-dev-server
将打包好的文件放在了内存中bundle.js
放在内存中的好处是:由于需要实时打包编译,所以放在内存中速度会非常快http://localhost:8080/
网站,发现是一个文件夹的面板,需要点击到src目录下,才能打开我们的index首页,此时引用不到bundle.js文件,需要修改index.html中script的src属性为:
http://localhost:8080/
的时候直接访问到index首页,可以使用--contentBase src
指令来修改dev指令,指定启动的根目录:"dev": "webpack-dev-server --contentBase src"
同时修改index页面中script的src属性为
html-webpack-plugin
插件配置启动页面由于使用--contentBase
指令的过程比较繁琐,需要指定启动的目录,同时还需要修改index.html中script标签的src属性,所以推荐大家使用html-webpack-plugin
插件配置启动页面.
cnpm i html-webpack-plugin --save-dev
安装到开发依赖webpack.config.js
配置文件如下:// 导入处理路径的模块
var path = require('path');
// 导入自动生成HTMl文件的插件
var htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: path.resolve(__dirname, 'src/js/main.js'), // 项目入口文件
output: { // 配置输出选项
path: path.resolve(__dirname, 'dist'), // 配置输出的路径
filename: 'bundle.js' // 配置输出的文件名
},
plugins:[ // 添加plugins节点配置插件
new htmlWebpackPlugin({
template:path.resolve(__dirname, 'src/index.html'),//模板路径
filename:'index.html'//自动生成的HTML文件的名称
})
]
}
3.修改package.json
中script
节点中的dev指令如下:
"dev": "webpack-dev-server"
4.将index.html中script标签注释掉,因为html-webpack-plugin
插件会自动把bundle.js注入到index.html页面中!
注意:热更新在JS中表现的不明显,可以从一会儿要讲到的CSS身上进行介绍说明!
package.json
的script节点如下,其中--open
表示自动打开浏览器,--port 4321
表示打开的端口号为4321,--hot
表示启用浏览器热更新:"dev": "webpack-dev-server --hot --port 4321 --open"
webpack.config.js
文件,新增devServer
节点如下:devServer:{
hot:true,
open:true,
port:4321
}
2.在头部引入webpack
模块:
var webpack = require('webpack');
在plugins
节点下新增:
new webpack.HotModuleReplacementPlugin()
cnpm i less-loader less -D
webpack.config.js
这个配置文件:{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
cnpm i sass-loader node-sass --save-dev
webpack.config.js
中添加处理sass文件的loader模块:{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }
cnpm i url-loader file-loader --save-dev
webpack.config.js
中添加处理url路径的loader模块:{ test: /\.(png|jpg|gif)$/, use: 'url-loader' }
limit
指定进行base64编码的图片大小;只有小于指定字节(byte)的图片才会进行base64编码:{ test: /\.(png|jpg|gif)$/, use: 'url-loader?limit=43960' },
cnpm i babel-core babel-loader babel-plugin-transform-runtime --save-dev
安装babel的相关loader包cnpm i babel-preset-es2015 babel-preset-stage-0 --save-dev
安装babel转换的语法webpack.config.js
中添加相关loader模块,其中需要注意的是,一定要把node_modules
文件夹添加到排除项:{ test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }
.babelrc
文件,并修改这个配置文件如下:{
"presets":["es2015", "stage-0"],
"plugins":["transform-runtime"]
}
babel-preset-es2015
可以更新为babel-preset-env
,它包含了所有的ES相关的语法;有时候使用npm i node-sass -D
装不上,这时候,就必须使用 cnpm i node-sass -D
运行cnpm i vue -S
将vue安装为运行依赖;
运行cnpm i vue-loader vue-template-compiler -D
将解析转换vue的包安装为开发依赖;
运行cnpm i style-loader css-loader -D
将解析转换CSS的包安装为开发依赖,因为.vue文件中会写CSS样式;
在webpack.config.js
中,添加如下module
规则:
module: {
rules: [
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
{ test: /\.vue$/, use: 'vue-loader' }
]
}
App.js
组件页面:
这是APP组件 - {{msg}}
我是h3
main.js
入口文件: // 导入 Vue 组件
import Vue from 'vue'
// 导入 App组件
import App from './components/App.vue'
// 创建一个 Vue 实例,使用 render 函数,渲染指定的组件
var vm = new Vue({
el: '#app',
render: c => c(App)
});
webpack.config.js
中添加resolve
属性:resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
}
使用 export default
和 export
导出模块中的成员; 对应ES5中的 module.exports
和 export
使用 import ** from **
和 import '路径'
还有 import {a, b} from '模块标识'
导入其他模块
使用箭头函数:(a, b)=> { return a-b; }
vue-router官网
import VueRouter from 'vue-router'
Vue.use(VueRouter);
import login from './components/account/login.vue'
import register from './components/account/register.vue'
var router = new VueRouter({
routes: [
{ path: '/', redirect: '/login' },
{ path: '/login', component: login },
{ path: '/register', component: register }
]
});
var vm = new Vue({
el: '#app',
// render: c => { return c(App) }
render(c) {
return c(App);
},
router // 将路由对象,挂载到 Vue 实例上
});
router-link
和router-view
: 登录
注册
Github 仓储地址
Mint-UI官方文档
import MintUI from 'mint-ui'
import 'mint-ui/lib/style.css'
Vue.use(MintUI)
primary
官网首页
文档地址
import '../lib/mui/css/mui.min.css'
webpack.config.js
中添加新的loader规则:{ test: /\.(png|jpg|gif|ttf)$/, use: 'url-loader' }
点击头像 -> 修改资料 -> SSH公钥 如何生成SSH公钥
创建自己的空仓储,使用 git config --global user.name "用户名"
和 git config --global user.email ***@**.com
来全局配置提交时用户的名称和邮箱
使用 git init
在本地初始化项目
使用 touch README.md
和 touch .gitignore
来创建项目的说明文件和忽略文件;
使用 git add .
将所有文件托管到 git 中
使用 git commit -m "init project"
将项目进行本地提交
使用 git remote add origin 仓储地址
将本地项目和远程仓储连接,并使用origin最为远程仓储的别名
使用 git push -u origin master
将本地代码push到仓储中
头部的固定导航栏使用 Mint-UI
的 Header
组件;
底部的页签使用 mui
的 tabbar
;
购物车的图标,使用 icons-extra
中的 mui-icon-extra mui-icon-extra-cart
,同时,应该把其依赖的字体图标文件 mui-icons-extra.ttf
,复制到 fonts
目录下!
将底部的页签,改造成 router-link
来实现单页面的切换;
Tab Bar 路由激活时候设置高亮的两种方式:
.router-link-active{
color:#007aff !important;
}
new VueRouter
的时候,通过 linkActiveClass
来指定高亮的类: // 创建路由对象
var router = new VueRouter({
routes: [
{ path: '/', redirect: '/home' }
],
linkActiveClass: 'mui-active'
});
将 tabbar 改造成 router-link
形式,并指定每个连接的 to
属性;
在入口文件中导入需要展示的组件,并创建路由对象:
// 导入需要展示的组件
import Home from './components/home/home.vue'
import Member from './components/member/member.vue'
import Shopcar from './components/shopcar/shopcar.vue'
import Search from './components/search/search.vue'
// 创建路由对象
var router = new VueRouter({
routes: [
{ path: '/', redirect: '/home' },
{ path: '/home', component: Home },
{ path: '/member', component: Member },
{ path: '/shopcar', component: Shopcar },
{ path: '/search', component: Search }
],
linkActiveClass: 'mui-active'
});
lunbo: [ 'http://www.itcast.cn/images/slidead/BEIJING/2017440109442800.jpg', 'http://www.itcast.cn/images/slidead/BEIJING/2017511009514700.jpg', 'http://www.itcast.cn/images/slidead/BEIJING/2017421414422600.jpg'
]
<div class="home-swipe">
<mt-swipe :auto="4000">
<mt-swipe-item v-for="(item, i) in lunbo" :key="i">
<img :src="item" alt="">
mt-swipe-item>
mt-swipe>
div>
div>
.vue
组件中使用vue-resource
获取数据运行cnpm i vue-resource -S
安装模块
导入 vue-resource 组件
import VueResource from 'vue-resource'
Vue.use(VueResource);
tab-top-webview-main
完成分类滑动栏router-link
身上的类名 mui-tab-item
存在兼容性问题,导致tab栏失效,可以把mui-tab-item
改名为mui-tab-item1
,并复制相关的类样式,来解决这个问题; .mui-bar-tab .mui-tab-item1.mui-active {
color: #007aff;
}
.mui-bar-tab .mui-tab-item1 {
display: table-cell;
overflow: hidden;
width: 1%;
height: 50px;
text-align: center;
vertical-align: middle;
white-space: nowrap;
text-overflow: ellipsis;
color: #929292;
}
.mui-bar-tab .mui-tab-item1 .mui-icon {
top: 3px;
width: 24px;
height: 24px;
padding-top: 0;
padding-bottom: 0;
}
.mui-bar-tab .mui-tab-item1 .mui-icon~.mui-tab-label {
font-size: 11px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
tab-top-webview-main
组件第一次显示到页面中的时候,无法被滑动的解决方案:import mui from '../../../lib/mui/js/mui.min.js'
mounted
事件钩子中,注册 mui 的滚动事件: mounted() {
// 需要在组件的 mounted 事件钩子中,注册 mui 的 scroll 滚动事件
mui('.mui-scroll-wrapper').scroll({
deceleration: 0.0005 //flick 减速系数,系数越大,滚动速度越慢,滚动距离越小,默认值0.0006
});
}
Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080
解决方法,可以加上* { touch-action: none; } 这句样式去掉。
原因:(是chrome为了提高页面的滑动流畅度而新折腾出来的一个东西) http://www.cnblogs.com/pearl07/p/6589114.html
https://developer.mozilla.org/zh-CN/docs/Web/CSS/touch-action
babel-plugin-transform-remove-strict-mode
一个Vue集成PhotoSwipe图片预览插件
要让apache支持gzip功能,要用到deflate_Module和headers_Module。打开apache的配置文件httpd.conf,大约在105行左右,找到以下两行内容:(这两行不是连续在一起的)
#LoadModule deflate_module modules/mod_deflate.so
#LoadModule headers_module modules/mod_headers.so
然后将其前面的“#”注释删掉,表示开启gzip压缩功能。开启以后还需要进行相关配置。在httpd.conf文件的最后添加以下内容即可:
#必须的,就像一个开关一样,告诉apache对传输到浏览器的内容进行压缩
SetOutputFilter DEFLATE
DeflateCompressionLevel 9
最少需要加上以上内容,才可以生gzip功能生效。由于没有做其它的额外配置,所以其它相关的配置均使用Apache的默认设置。这里说一下参数“DeflateCompressionLevel”,它表示压缩级别,值从1到9,值越大表示压缩的越厉害。
{ path: '/', redirect: '/home' }
],
linkActiveClass: 'mui-active'
});
## 实现 tabbar 页签不同组件页面的切换
1. 将 tabbar 改造成 `router-link` 形式,并指定每个连接的 `to` 属性;
2. 在入口文件中导入需要展示的组件,并创建路由对象:
// 导入需要展示的组件
import Home from ‘./components/home/home.vue’
import Member from ‘./components/member/member.vue’
import Shopcar from ‘./components/shopcar/shopcar.vue’
import Search from ‘./components/search/search.vue’
// 创建路由对象
var router = new VueRouter({
routes: [
{ path: ‘/’, redirect: ‘/home’ },
{ path: ‘/home’, component: Home },
{ path: ‘/member’, component: Member },
{ path: ‘/shopcar’, component: Shopcar },
{ path: ‘/search’, component: Search }
],
linkActiveClass: ‘mui-active’
});
## 使用 mt-swipe 轮播图组件
1. 假数据:
lunbo: [ ‘http://www.itcast.cn/images/slidead/BEIJING/2017440109442800.jpg’, ‘http://www.itcast.cn/images/slidead/BEIJING/2017511009514700.jpg’, ‘http://www.itcast.cn/images/slidead/BEIJING/2017421414422600.jpg’
]
2. 引入轮播图组件:
```html
.vue
组件中使用vue-resource
获取数据运行cnpm i vue-resource -S
安装模块
导入 vue-resource 组件
import VueResource from 'vue-resource'
Vue.use(VueResource);
tab-top-webview-main
完成分类滑动栏router-link
身上的类名 mui-tab-item
存在兼容性问题,导致tab栏失效,可以把mui-tab-item
改名为mui-tab-item1
,并复制相关的类样式,来解决这个问题; .mui-bar-tab .mui-tab-item1.mui-active {
color: #007aff;
}
.mui-bar-tab .mui-tab-item1 {
display: table-cell;
overflow: hidden;
width: 1%;
height: 50px;
text-align: center;
vertical-align: middle;
white-space: nowrap;
text-overflow: ellipsis;
color: #929292;
}
.mui-bar-tab .mui-tab-item1 .mui-icon {
top: 3px;
width: 24px;
height: 24px;
padding-top: 0;
padding-bottom: 0;
}
.mui-bar-tab .mui-tab-item1 .mui-icon~.mui-tab-label {
font-size: 11px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
tab-top-webview-main
组件第一次显示到页面中的时候,无法被滑动的解决方案:import mui from '../../../lib/mui/js/mui.min.js'
mounted
事件钩子中,注册 mui 的滚动事件: mounted() {
// 需要在组件的 mounted 事件钩子中,注册 mui 的 scroll 滚动事件
mui('.mui-scroll-wrapper').scroll({
deceleration: 0.0005 //flick 减速系数,系数越大,滚动速度越慢,滚动距离越小,默认值0.0006
});
}
Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080
解决方法,可以加上* { touch-action: none; } 这句样式去掉。
原因:(是chrome为了提高页面的滑动流畅度而新折腾出来的一个东西) http://www.cnblogs.com/pearl07/p/6589114.html
https://developer.mozilla.org/zh-CN/docs/Web/CSS/touch-action
babel-plugin-transform-remove-strict-mode
一个Vue集成PhotoSwipe图片预览插件
要让apache支持gzip功能,要用到deflate_Module和headers_Module。打开apache的配置文件httpd.conf,大约在105行左右,找到以下两行内容:(这两行不是连续在一起的)
#LoadModule deflate_module modules/mod_deflate.so
#LoadModule headers_module modules/mod_headers.so
然后将其前面的“#”注释删掉,表示开启gzip压缩功能。开启以后还需要进行相关配置。在httpd.conf文件的最后添加以下内容即可:
#必须的,就像一个开关一样,告诉apache对传输到浏览器的内容进行压缩
SetOutputFilter DEFLATE
DeflateCompressionLevel 9
最少需要加上以上内容,才可以生gzip功能生效。由于没有做其它的额外配置,所以其它相关的配置均使用Apache的默认设置。这里说一下参数“DeflateCompressionLevel”,它表示压缩级别,值从1到9,值越大表示压缩的越厉害。
注意:由于默认使用的美国的服务器进行中间转接,所以访问速度炒鸡慢,访问时可启用FQ软件,提高网页打开速度!