简单Vue实例
el:DOM的负责区域
data:定义数据
<script>
var app = new Vue({
el: '#app',
data: {
content:'hello world'
}
})
</script>
Vue生命周期
生命周期函数就是Vue实例在某一个时间点会自动执行的函数
v-for 循环
<ul>
<li v-for="item of list">{{item}}</li>
</ul>
<script>
var app = new Vue({
el: '#app',
data: {
list: ['111','222','333']
}
})
</script>
v-on 事件绑定、监听
使用v-on,引号内为表达式或者变量
"v-on:click" 可写作 "@click"
methods:方法
<button v-on:click="handleBtnClick">
提交
</button>
<script>
var app = new Vue({
el: '#app',
methods: {
handleBtnClick: function() {
alert('click')
}
}
})
</script>
v-model 双向数据绑定
<input type="text" v-model="inputValue" />
<script>
var app = new Vue({
el: '#app',
data: {
inputValue: ''
}
})
</script>
v-bind 属性绑定,简写:
v-text和v-html
v-text 文本绑定,自动转义,纯文本
v-html 不转义,做解析
<div id="app">
<div v-text="name"></div>
<div v-html="name"></div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
name: "<h1>hello world</h1>"
}
})
</script>
效果:
计算属性 computed
如果依赖的属性(firstName 和 lastName)不变,则使用上一次计算的缓存,提高性能
<div id="#app">
<div>
{{fullName}}
{{age}}
</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
firstName: 'Solitary',
lastName: 'Su'
age: "20"
},
computed: {
fullName: function() {
return this.firstName + ' ' + this.lastName
}
}
})
</script>
getter和setter方法
<div id="#app">
<div>
{{fullName}}
{{age}}
</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
firstName: 'Solitary',
lastName: 'Su'
age: "20"
},
computed: {
fullName: {
get: function() {
return this.firstName + ' ' + this.lastName
}
set: function(value) {
console.log(value)
}
}
}
})
</script>
监听器 watch
如果watch,computed都可以实现,建议使用computed,性能高
<div id="app">
<div>
{{fullName}}
</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
firstName: 'Solitary',
lastName: 'Su',
fullName: 'Solitary Su',
},
watch: {
firstName: function() {
this.fullName = this.firstName + " " + this.lastName;
},
lastName: function() {
this.fullName = this.firstName + " " + this.lastName;
}
}
})
</script>
样式绑定
方式1:对象绑定
<div id="app">
<div @click="handleDivClick" :class="{activated: isActivated}">
Hello world
</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
isActivated: false,
},
methods: {
handleDivClick: function () {
this.isActivated = !this.isActivated;
},
},
});
</script>
方式2:数组绑定
<div id="app">
<div @click="handleDivClick" :class="[activated]">Hello world</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
activated: "",
},
methods: {
handleDivClick: function () {
this.activated = this.activated === "activated" ? "" : "activated";
},
},
});
</script>
方式3:style操作
<div id="app">
<div :style="[styleObj, {fontSize: '20px'}]" @click="handleDivClick">Hello world</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
styleObj: {
color: "black",
},
},
methods: {
handleDivClick: function () {
this.styleObj.color =
this.styleObj.color === "red" ? "black" : "red";
},
},
});
</script>
v-if和v-show
<div id="app">
<div v-if="show">{{message}}</div>
<div v-show="show">{{message}}</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
show: false,
message: "Hello world"
},
})
</script>
v-if和v-show区别:
-
v-if,如果是false,从DOM中移除
-
v-show,如果是false,会被css被隐藏
频率高用v-show,不会销毁DOM,性能更高
频率不高用v-if
v-else、v-else-if
<div id="app">
<div v-if="show === 'a'">This is A</div>
<div v-else-if="show === 'b'">This is B</div>
<div v-else>This is others</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
show: "a",
message: "Hello World"
},
})
</script>
key
vue渲染过程中,尽量复用原有元素。vue根据key值识别唯一元素。
列表渲染
不推荐使用index作为key值,通常后端向前端传输数据会有唯一的标识符。
数组循环:
<div id="app">
<div v-for="(item, index) of list" :key="item.id">
{{item.text}} ---- {{index}}
</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
list: [
{
id: "01010101",
text: "hello",
},
{
id: "01010102",
text: "solitary",
},
{
id: "01010103",
text: "su",
},
],
},
});
</script>
尝试修改数组内容时,不能使用下标修改,即vm.list[3] = {}
vue提供7种变异方法:
- pop 删除
- push 增加
- shift 第一项删除
- unshift 第一项增加内容
- splice 数组截取
- sort 排序
- reverse 取反
vm.list.splice(1, 1, {id:"333", text: "solitary1"})
vm.list = [
{
id: "01010101",
text: "hello",
},{
id: "01010102",
text: "solitary1",
},
{
id: "01010103",
text: "su",
}]
对象循环:
<div id="app">
<div v-for="(item, key, index) of userInfo">
{{item}} --- {{key}} --- {{index}}
</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
userInfo: {
name: "solitary",
age: 20,
gander: "male",
salary: "secret"
}
},
});
</script>
对象动态加值不可行
vm.userInfo = {
name: "solitary",
age: 20,
gander: "male",
salary: "secret",
address: "beijing"
}
template 占位符
template标签不出现在html中
<div id="app">
<template v-for="(item, index) of list" :key="item.id">
<div>{{item.text}} ---- {{index}}</div>
<span> {{item.text}} </span>
</template>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
list: [
{
id: "01010101",
text: "hello",
},
{
id: "01010102",
text: "solitary",
},
{
id: "01010103",
text: "su",
},
],
},
});
</script>
Vue中的set方法
Vue.set(vm.userInfo,"address","beijing")
vm.$set(vm.userInfo,"address","beijing")
is属性
<div id="root">
<table>
<tbody>
<tr is="row"></tr>
<tr is="row"></tr>
<tr is="row"></tr>
</tbody>
</table>
</div>
<script>
Vue.component("row", {
template: "<tr><td>this is a row</td></tr>",
});
var vm = new Vue({
el: "#root",
});
</script>
符合h5编码规范
子组件定义data使用函数
在根组件里面,使用对象方式定义data没有问题,子组件不能这么定义。子组件要求是函数,不能是对象。每个子组件拥有独立的数据存储,不会出现多个子组件互相影响。
Vue.component("counter", {
data: function() {
return {
number: 0
}
}
})
通过ref操作DOM
this.$refs.ref
<div id="root">
<div ref="hello" @click="handleClick">hello world</div>
</div>
<script>
var vm = new Vue({
el: "#root",
methods: {
handleClick: function() {
this.$refs.hello.innerHTML = "123"
}
}
});
</script>
子组件向父组件传值
<counter @change="handleChange"></counter>
this.$emit("change", this.content)
父子组件的数据传输
父组件向子组件传值
<div id="root">
<counter :count="0"></counter>
<counter :count="1"></counter>
</div>
<script>
var counter = {
props: ["count"],
data: function () {
return {
number: this.count,
};
},
template: "<div @click='handleClick'>{{number}}</div>",
methods: {
handleClick: function () {
this.number++;
},
},
};
var vm = new Vue({
el: "#root",
components: {
counter: counter,
},
});
</script>
单向数据流,父组件可以通过属性的形式给子组件传递数据,父组件可以进行随意修改,子组件不能修改父组件的参数。
因为如果接受的是Object形式,子组件修改后,如果被其他子组件使用,对其他子组件造成影响。
子组件向父组件传值
<div id="root">
<counter :count="3" @inc="handleIncrease"></counter>
<counter :count="2" @inc="handleIncrease"></counter>
<div>{{total}}</div>
</div>
<script>
var counter = {
props: ["count"],
data: function () {
return {
number: this.count,
};
},
template: "<div @click='handleClick'>{{number}}</div>",
methods: {
handleClick: function () {
this.number++;
this.$emit("inc", 1);
},
},
};
var vm = new Vue({
el: "#root",
data: {
total: 5,
},
components: {
counter: counter,
},
methods: {
handleIncrease: function (step) {
this.total += step;
},
},
});
</script>
组件参数校验
<div id="root">
<child :content="'123'"></child>
</div>
<script>
Vue.component("child", {
props: {
content: {
// 类型
type: String,
// 是否需要传值
required: false,
// 默认值
default: "default value",
// 自定义校验器
validator: function(value) {
return (value.length > 5)
}
},
},
template: "<div>{{content}}</div>",
});
var vm = new Vue({
el: "#root",
});
</script>
props特性与非props特性
props特性:
当父组件使用子组件时,通过属性向子组件传值,恰好子组件声明了对父组件传递的接收,即父组件传递了content,子组件声明了props: content,具有对应关系。
特点:
- 不会在DOM标签中显示
- 在子组件可以通过插值表达式或者this取得内容,显示出来
非props特性
父组件向子组件传递属性,但是没有声明。
- 没法获取内容
- 会显示在HTML属性中
给组件绑定原生事件
正常监听内部组件向外触发的自定义事件,因此需要两层传递
<div id="root">
<child @click="handleClick"></child>
</div>
<script>
Vue.component("child", {
template: "<div @click='handleChildClick'>Child</div>",
methods: {
handleChildClick: function() {
this.$emit("click")
}
}
})
var vm = new Vue({
el: "#root",
methods: {
handleClick: function() {
alert("233")
}
}
})
</script>
监听原生点击事件 @click.native
<div id="root">
<child @click.native="handleClick"></child>
</div>
<script>
Vue.component("child", {
template: "<div>Child</div>"
})
var vm = new Vue({
el: "#root",
methods: {
handleClick: function() {
alert("233")
}
}
})
</script>
非父子组件间的传值(bus/总线/发布订阅模式/观察者模式)
解决方法:
- Vuex
- 发布定义模式,总线机制
<div id="root">
<child content="Solitary"></child>
<child content="Su"></child>
</div>
<script>
Vue.prototype.bus = new Vue();
Vue.component("child", {
data: function () {
return {
selfContent: this.content,
};
},
props: {
content: String,
},
template: "<div @click='handleClick'>{{selfContent}}</div>",
methods: {
handleClick: function () {
this.bus.$emit("change", this.selfContent);
},
},
mounted: function () {
var this_ = this;
this.bus.$on("change", function (msg) {
this_.selfContent = msg;
});
},
});
var vm = new Vue({
el: "#root",
});
</script>
在Vue中使用插槽(slot)
content不好用的时候,部分内容需要外部传递。
<div id="root">
<body-content>
<div class="header" slot="header">header</div>
<div class="footer" slot="footer">footer</div>
</body-content>
</div>
<script>
Vue.component("body-content", {
template: `<div>
<slot name="header">
<h1>
default header
</h1>
</slot>
<div class='content'>content</div>
<slot name="footer"></slot>
</div>`,
});
var vm = new Vue({
el: "#root",
});
</script>
Vue中的作用域插槽
传递作用域插槽,必须由<template>
包裹,声明子组件接收的数据放在哪,告诉子组件模板信息。
子组件做循环,或者dom结构由外部结构传进来的时候可以使用。子组件可以向父组件插槽传数据,父组件接收数据必须使用template和slot-scope。
<div id="root">
<child>
<template slot-scope="props">
<li>{{props.item}} - hello</li>
</template>
</child>
</div>
<script>
Vue.component("child", {
data: function () {
return {
list: [1, 2, 3, 4],
};
},
template: `
<div>
<ul>
<slot v-for="item of list" :item=item></slot>
</ul>
</div>
`,
});
var vm = new Vue({
el: "#root",
});
</script>
动态组件(component标签)
使用一个按钮,使得显示内容不断变化
使用v-if
<div id="root">
<child-one v-if="type === 'child-one'"></child-one>
<child-two v-if="type === 'child-two'"></child-two>
<button @click="handleBtnClick">change</button>
</div>
<script>
Vue.component("child-one", {
template: "<div>child-one</div>",
});
Vue.component("child-two", {
template: "<div>child-two</div>",
});
var vm = new Vue({
el: "#root",
data: {
type: "child-one",
},
methods: {
handleBtnClick: function () {
this.type = this.type === "child-one" ? "child-two" : "child-one";
},
},
});
</script>
使用component标签
<div id="root">
<component :is="type"></component>
<button @click="handleBtnClick">change</button>
</div>
<script>
Vue.component("child-one", {
template: "<div>child-one</div>",
});
Vue.component("child-two", {
template: "<div>child-two</div>",
});
var vm = new Vue({
el: "#root",
data: {
type: "child-one",
},
methods: {
handleBtnClick: function () {
this.type = this.type === "child-one" ? "child-two" : "child-one";
},
},
});
</script>
v-once指令
可以把反复销毁的组件放入内存,提高性能
<div id="root">
<child-one v-if="type === 'child-one'"></child-one>
<child-two v-if="type === 'child-two'"></child-two>
<button @click="handleBtnClick">change</button>
</div>
<script>
Vue.component("child-one", {
template: "<div v-once>child-one</div>",
});
Vue.component("child-two", {
template: "<div v-once>child-two</div>",
});
var vm = new Vue({
el: "#root",
data: {
type: "child-one",
},
methods: {
handleBtnClick: function () {
this.type = this.type === "child-one" ? "child-two" : "child-one";
},
},
});
</script>
环境准备
下载安装nodejs:http://nodejs.cn/download/
教程:https://www.cnblogs.com/aizai846/p/11441693.html
构建Vue项目并运行
npm install -g vue-cli
vue init webpack my-app # 创建项目
cd my-app
npm run dev
创建的时候会问一堆问题:
Project name ==> 项目名称,直接回车
Project description ==> 项目描述,可以直接回车
Author ==> 作者,也可以直接回车
Vue build (Use arrow keys) ==> 构建方式,选 Runtime + Compiler: recommended for most users,即直接回车
Install vue-router? (Y/n) ==> 是否需要路由,输入Y回车
Use ESLint to lint your code? ==> 是否使用ESLint语法检测,输入Y回车
Pick an ESLint preset (Use arrow keys) ==> 直接回车
Set up unit tests ==> 是否安装单元测试工具,目前我们不需要,所以n回车
Setup e2e tests with Nightwatch ==> 是否需要端到端测试工具,目前我们不需要,所以n回车
Should we run npm install
for you after the project has been created? ==> 我用的npm
项目目录
-
static目录:静态资源,图片,json数据
-
node_modules目录:第三方node包
-
src目录:源代码
- main.js文件:整个项目的入口文件
- app.vue文件:根组件
- router目录:路由
- components目录:小组件
- pages目录:自行创建,每个文件夹对应单个页面
- assets目录:图片资源
-
config目录:项目配置文件,基础配置信息放在index.js,开发环境配置信息放在dev.env.js,线上环境的配置信息放在prod.env.js
-
build目录:webpack打包的配置内容
Vue单文件组件
vue结尾文件,实际上放的是一个vue组件
Vue中的路由
路由就是根据网址的不同,返回不同的内容给用户
<router-view>
显示的是当前路由地址所对应的内容
<router-link>
跳转,原理为在div外部套a标签,可用css强制变色
多页应用和单页应用
多页应用
原理:页面跳转 → 返回HTML
优点:首屏时间快,SEO效果好
缺点:页面切换慢
单页应用
原理:页面跳转 → JS渲染
优点:页面切换快
缺点:首屏时间稍慢,SEO差
开发过程
移动端开发项目初始化
配置index.html中的viewport
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
引入reset.css:重置页面样式表,解决不同手机浏览器样式不同的问题
引入boder.css:解决多倍屏1像素边框问题
引入fastclick库:解决点击延迟问题:
npm install fastclick --save
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'
import fastClick from 'fastclick'
import './assets/styles/reset.css'
import './assets/styles/border.css'
Vue.config.productionTip = false
fastClick.attach(document.body)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
ajax
使用axios
npm install axios --save
在static目录下创建mock文件夹,添加index.json
一般来说本地json不上传git,在.gitignore文件,上半部分加入
static/mock
方便上线,加入转发机制,由webpack dev工具提供
打开config/index.js
dev:proxyTable
proxyTable: {
'/api': {
target: 'http://localhost:8080',
pathRewriter: {
'^/api': '/static/mock'
}
}
}
Vuex
在src下创建store文件夹,新建index.js
内网穿透
出现 Invalid Host header
打开build目录下的webpack.base.config.js
devServer: {
disableHostCheck: true,
},
低版本安卓出现白屏
可能1:
npm install babel-polyfill --save
可能2:dev server的问题
Q.E.D.