Vue
一. 创建vue项目
tianqideMBP:try-vue tianqizhao$ vue init webpack hello
? Project name hello
? Project description A Vue.js project
? Author tianqi-zhao <tianqizhao.judy@gmail.com>
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset (Use arrow keys)
❯ Standard (https://github.com/standard/standard)
Airbnb (https://github.com/airbnb/javascript)
none (configure it yourself)
? Set up unit tests Yes
? Pick a test runner (Use arrow keys)
❯ Jest
Karma and Mocha
none (configure it yourself)
? Setup e2e tests with Nightwatch? Yes
? Should we run `npm install` for you after the project has been created? (recom
mended) (Use arrow keys)
❯ Yes, use NPM
Yes, use Yarn
No, I will handle that myself
vue-cli · Generated "hello".
# Installing project dependencies ...
# ========================
二. 先来个例子感受一下
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id = "app">
<h1>{{mymessage}}</h1>
<input v-model="mymessage" />
</div>
</body>
<!-- MV VM 双向绑定-->
<script>
var vue = new Vue(
{
el:'#app',
data:{
mymessage:"hello world"
}
}
)
</script>
</html>
这种方式使用的vue与html中的js的变量对象相似,是html先加载到界面上,之后使用vue对象对其进行更改。
vue对象的属性el是用来指定其能更改的部分,所以需要将div绑定id,并查找这个id带指的标签,交给vue进行控制。
vue构造函数通过传入一个json对象初始化其内部内容。
三. vue生命周期
<!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>vue生命周期学习</title>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{message}}</h1>
<input v-model="message" />
</div>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Vue的生命周期'
},
beforeCreate: function() {
console.group('------调用钩子函数beforeCreate------');
console.log("%c%s", "color:red" , "el : " + this.$el); //undefined
console.log("%c%s", "color:red","data : " + this.$data); //undefined
console.log("%c%s", "color:red","message: " + this.message)
},
created: function() {
console.group('------调用钩子函数created------');
console.log("%c%s", "color:red","el : " + this.$el); //undefined
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
beforeMount: function() {
console.group('------调用钩子函数beforeMount------');
console.log("%c%s", "color:red","el : " + (this.$el)); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
mounted: function() {
console.group('------调用钩子函数mounted------');
console.log("%c%s", "color:red","el : " + this.$el); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
beforeUpdate: function () {
console.group('调用钩子函数beforeUpdate===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
updated: function () {
console.group('调用钩子函数updated===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
beforeDestroy: function () {
console.group('调用钩子函数beforeDestroy===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
destroyed: function () {
console.group('调用钩子函数destroyed===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message)
}
})
</script>
</html>
------调用钩子函数beforeCreate------
el : undefined
data : undefined
message: undefined
init injections & reactivity 这步初始化data和message
------调用钩子函数created------
el : undefined
data : [object Object]
message: Vue的生命周期
在调用钩子函数beforeMount之前,检测el标签所指代的内容,并将outerHTML编译为模板,将模板编译为render函数
------调用钩子函数beforeMount------
el : [object HTMLDivElement]
<div id="app">
data : [object Object]
message: Vue的生命周期
根据之前的outerHTML/template/render()渲染得到的dom树,创建vm.$el并赋值给el元素所指代部分
------调用钩子函数mounted------
el : [object HTMLDivElement]
<div id="app">
data : [object Object]
message: Vue的生命周期
此时结束后,页面已经处于Mounted状态了,接下来就等待有更改发生,才会触动其他钩子事件:
更改input内容为“Vue的生命周“,上方h1标签和input内容message绑定,此时触发状态转变,调用钩子函数beforeUpdate()
调用钩子函数beforeUpdate===============》
el : [object HTMLDivElement]
<div id="app">
innerHTML: "<h1>Vue的生命周期</h1> <input>"
innerText: "Vue的生命周期"
data : [object Object]
Virtual DOM re-render and patch:根据vue对象内部更改的信息,更改虚拟DOM树,对DOM树“打补丁”进行修改
调用钩子函数updated===============》
el : [object HTMLDivElement]
<div id="app">
innerHTML: "<h1>Vue的生命周</h1> <input>"
innerText: "Vue的生命周"
data : [object Object]
message: Vue的生命周
四. 指令
4.1 View视图模板部分:
4.1.1 数据绑定:
4.1.1.1 v-bind
title标签是鼠标悬停到上方时,有标签“提示”弹出来。
vue框架将title属性的内容绑定给Vue:data里面的变量
例子一:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="app">
<div v-bind:title="mymessage">
try2
</div>
<div :title="mymessage">
try
</div>
</div>
</body>
<script>
var vue = new Vue({
el:'#app',
data:{
mymessage:"hello world",
}
})
</script>
</html>
例子二:
v-bind可以绑定class,修改标签style类型
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<style>
.container{
width: 100px;
height: 100px;
}
.state1{
background-color: red;
}
.state2{
background-color: yellow;
}
.state3{
background-color: #008000;
}
</style>
<body>
<div id="app">
<button @click="change">红绿灯</button>
<div :class="{container:true,state1:state==0,state2:state==1,state3:state==2}">
</div>
</div>
</body>
<script>
var vue = new Vue({
el:'#app',
data:{
state:0
},
methods:{
change(){
this.state = (this.state+1)%3
}
}
})
</script>
</html>
4.1.1.2 v-model
v-bind和v-model的区别在于:
v-bind为单向绑定,是MV模式,只是将model里面的内容显示在view视图上。其简写为:value='model数据'
v-model是双向绑定,是MV VM模式,多出来的VM是指view视图的变化会影响model。
例子一:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
<input v-model="text" />
<input :value="text" />
</div>
</body>
<script>
var vue = new Vue({
el:'#app',
data:{
text:"v-model"
}
})
</script>
</html>
效果:
例子二:
form表单使用v-model
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
<form>
java<input type="checkbox" name="likes" value="java" v-model="likes"/>
css<input type="checkbox" name="likes" value="css" v-model="likes"/>
c++<input type="checkbox" name="likes" value="c++" v-model="likes"/>
<div></div>
男<input type="radio" name="gender" v-model="gender" value="male"/>
女<input type="radio" name="gender" v-model="gender" value="female"/>
<select v-model="countryid">
<option v-for="item in conutries" :value="item.key">
{{item.name}}
</option>
</select>
</form>
</div>
</body>
<script>
var vue = new Vue({
el:'#app',
data:{
likes:["java","css","c++"],
gender:"female",
conutries:[
{key:1,name:"America"},
{key:2,name:"China"},
{key:3,name:"Japan"},
],
countryid:2
}
})
</script>
</html>
Checkbox中v-model的值为选定的多个值
radio中v-model的值为选定的单个值
select中v-model设置option中的value进行选择
4.1.2 v-if
效果是页面初始状态为“登陆”字符,点击之后,变为“登出”。反复点击反复跳转
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="app">
<button v-on:click="login">
<span v-if="!islogin">
登陆
</span>
<span v-if="islogin">
登出
</span>
</button>
</div>
</body>
<script>
var vue = new Vue({
el:'#app',
data:{
islogin:false
},
methods:{
login(){
this.islogin = !this.islogin;
}
}
})
</script>
</html>
4.1.3 v-for
v-for="(people,i) in list",people为list里面的item,i为索引
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="(people,i) in list">
{{i}}--{{people.name}},{{people.age}}
</li>
</ul>
<table>
<tbody>
<tr v-for="(people,i) in list">
<td>{{i}}</td>
<td>{{people.name}}</td>
<td>{{people.age}}</td>
</tr>
</tbody>
</table>
</div>
</body>
<script>
var vue = new Vue({
el:'#app',
data:{
list:[
{
name:"哥哥",
age:22
},
{
name:"姐姐",
age:18
},
{
name:"小朋友",
age:8
}
]
}
})
</script>
</html>
4.1.4 v-on
v-on简写可以写为@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
<h1>{{message}}</h1>
<button @click="reverse">反过来</button>
</div>
</body>
<script>
var vue = new Vue({
el:'#app',
data:{
message:"你真棒"
},
methods:{
reverse(){
this.message = this.message.split('').reverse().join('')
}
}
})
</script>
</html>
效果是点击按钮反转“你真棒”为“棒真你”
4.2 Model模型部分指令:
4.2.1 computed
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
<input v-model="price" />
<h>{{readblePrice}}</h><!-- 需要计算的参数 -->
</div>
</body>
<script>
var vue = new Vue({
el:'#app',
data:{
price:0
},
computed:{
readblePrice:function(){
return this.dealNumberToMoney(this.price)
}
},
methods:{
//将数字每三个用逗号隔开输出
dealNumberToMoney(money){
var fmtAmt = "";
if(money&&money!=null){
money = money.replace(/,/g,"").replace(/,/g,"");
if(money.indexOf(".")==-1){
money = money + ".00";
}
var index = money.indexOf(".");
var floatValue = money.substring(index+1);
var count = 0;
for(var i=(index);i>=0;i--){
fmtAmt = money[i]+fmtAmt;
if(count==3&&i!=0){
fmtAmt = "," + fmtAmt;
count = 0;
}
count++;
}
}
fmtAmt += floatValue;
return fmtAmt;
}
}
})
</script>
</html>
4.2.2 watched监听
当一个数据模型data发生变化时,进行某一种操作
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
<input v-model="husband" />
<input v-model="wife" />
<input :value="price" readonly="true"/>
<div>
{{state}}
</div>
</div>
</body>
<script>
var vue = new Vue({
el:'#app',
data:{
husband:"约翰",
wife:"",
state:"",
price:1000
},
watch:{
wife:function(){
if(this.wife == ""){
this.state = "单身狗"
}else{
this.state = "小情侣"
}
},
husband:function(){
this.price = this.price+100
}
}
})
</script>
</html>
效果:husband和wife两个input栏具有事件监听事件。husband变化,price栏数字加100。wife变化,若为空,则下部分div为“单身狗”,否则为“小情侣”。
五. 组件技术
首先需要先使用vue脚手架新建一个vue项目。(要求已经按照vue脚手架)
一个最初始的vue项目src主要有:assest(图片),components(vue文件),router(js路由文件),外层组件App.vue, main.js
程序初始运行时,首先进入main.js文件。
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
在js文件里面,创建了Vue对象,将其构造所需要的参数传入其中。每一个import的都是一个组件。
注意el标签提供的是template模板的挂载点。
在template模板App中定义了id为“app”的挂载点。
<template>
<div id="app">
<img src="./assets/login.png">
<router-view/>
</div>
</template>
组件App使用了,这个标签会根据url路径进行路由:
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
这个路由的意思是,当path为“/”时,将使用组件‘HelloWorld’替换。
通常情况下,我们的路径信息会跳转为:
而“#”表示单页应用,在其页面内进行路由。
整个过程,最为重要的就是将vue文件视作一个vue对象组件。而组件又可以在其他的vue组件中进行引用。
我将组件的引用加载技术分为两类:
5.1 通过路由加载组件
第一种方法是通过替换达到使用组件的目的。
如此加载组件的必要条件是:在router中将路径与组件进行匹配
目前我的router中的index.js文件中routes配置如下:
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/hello',
name: 'myHello',
component: MyHello
}
]
因此,在不同的输入地址下,它会显示出不同的内容(被不同组件替换)
5.2 直接对组件进行引用
很多时候,我们用不着跳转,只是想将组件分开写,使代码更有结构性,让组件可复用。
那么这个时候,就直接将外部组件在模型中import并声明,就可以渲染到template标签中了。
看个例子
<template>
<div>
<sun-sun></sun-sun><!-- 驼峰在这里面改成“-”,规范 -->
</div>
</template>
<script>
import sun from "../view/myComponents.vue"
export default {
name: "myHello",
data(){
},
components:{
sunSun:sun
}
}
</script>
<style>
</style>
注意:采用驼峰命名法是因为在webpack中所有名称都会被转变为小写
可以看到template,div中引用了名称为:sunSun的组件。在vue实例创建的过程中,会通过动态渲染,将该部分替换称为"…/view/myComponents.vue"这个组件。
<template>
<div>
<form>
省
<select id="province" v-model="pid">
<option v-for="(proItem,id) in provinces" :value="id">
{{proItem}}
</option>
</select>
市
<select id="city" v-model="cid">
<option v-for="(cityItem,id) in cities" :value="id">
{{cityItem}}
</option>
</select>
区
<select id="district" v-model="did">
<option v-for="(districtItem,id) in district" :value="id">
{{districtItem}}
</option>
</select>
</form>
</div>
</template>
<script>
import area from "../data/area.js"
export default{
name:"son",
data(){
return {
pid:"",
cid:"",
did:"",
provinces: area["0"],
cities: null,
district: null
}
},
watch:{
pid:function(){
var indexStrC = "0,"+this.pid
this.cities = area[indexStrC]
this.district = null
},
cid:function(){
var indexStrD = "0,"+this.pid+","+this.cid
this.district = area[indexStrD]
}
}
}
</script>
<style>
select{
width: 100px;
}
</style>
这个组件是一个省市区三级联动的下拉框
其数据存在"…/data/area.js"中
5.4 组件间的通信
组件间有两种通信方式,第一种是父组件向子组件传值,第二种是子组件向父组件传值。
外加一个比较特殊的插槽用法。
5.4.1 父组件向子组件传值
父组件向子组件传值很简单,父组件只需要在子组件处声明要传递的值,并为其赋值。(子组件名称为prop)
<prop mymsg="hello world"></prop>
子组件需要在模型部分使用props接受这个值,即可与data()内返回的数据具有相同的用法。
<template>
<div>
{{msg}}
</div>
</template>
<script>
export default{
name: "prop",
props:["mymsg"]//获取父组件传过来的值
}
</script>
5.4.2 子组件向父组件传值
子组件向父组件传值需要触发父组件的事件。
首先在子组件部分需要使用this.$emit
触发自定义的事件,这个函数需要传入事件的名称,以及要传输的数据
<template>
<div>
<input v-model="messageToFather" />
<button @click="sendMessgae">send</button>
</div>
</template>
<script>
export default{
name:"sendmessage",
data(){
return{
messageToFather:""
}
},
methods:{
sendMessgae(){
this.$emit("event",{sendMessgae:this.messageToFather})//核心函数
}
}
}
</script>
而父组件处需要将子组件与这个事件绑定,并进行相应:
<template>
<div>
<div>
<send-message @event="showMessage"></send-message><!-- 父组件绑定这个事件,并通过showMessage()进行响应 -->
<div v-model="sonMessage">
这里是子组件传过来的值:{{sonMessage}}
</div>
</div>
</template>
<script>
import sendMessgae from "../view/sendMessage.vue"
export default {
name: "myHello",
data(){
return {
sonMessage:""
}
},
methods:{
showMessage(data){
console.log(arguments)
this.sonMessage = data["sendMessgae"]
}
},
components:{
sendMessage:sendMessgae,
}
}
</script>
最后显示结果为:
点击send按钮,会想input的输入内容显示在下方。
5.4.3 插槽
插槽就是将子组件当作一个槽,插进去的是父组件提供的数据
子组件:
<template>
<div>
这里是子组件,下️面是父组件
<div>
<slot></slot><!-- 这里就是槽 -->
</div>
子组件
</div>
</template>
父组件
父组件将myslot.vue作为组件导入为mySlot,并在模板中使用。最后渲染的时候,将父组件的部分替换掉子组件的部分。并将子组件的所有替换掉父组件的部分
<template>
<div>
<my-slot>
我是父组件
</my-slot>
</div>
</template>
<script>
import mySlot from "../view/mySlot.vue"
export default {
name: "myHello",
components:{
mySlot:mySlot
}
}
</script>
效果:
其中作用域插槽需要特别注意,作用域插槽是数据由子组件提供,但是父组件可以使用子组件的数据
更多关于插槽:https://juejin.im/post/5a69ece0f265da3e5a5777ed