前端-09-vue
1.概述
渐进式,可以嵌入到现有程序,也可单独处理
响应式,数据变化引起视图更新
可复用组件,拥有自己的js css
vue更适合快速构建,适合中小型项目
不支持ie8及以下,支持所有支持es5的浏览器
调试工具 vue devtools
默认构建方案webpack
命令行工具vue cli,市场占有最多是2.x版,本教程基于2.x
vue兼具angular和react的有点
文档
https://cn.vuejs.org/v2/guide/
vue cli文档
https://cli.vuejs.org/zh/guide/
2.环境
版本,vue-cli改成@vue-cli
# 安装(速度略慢)
npm install -g @vue/cli
# 查看版本
vue -V
# 拉取旧版2.x
npm install -g @vue/cli-init
# 创建项目
vue init webpack 01-basic
# 选择npm
# 安装依赖
npm install
# 启动
npm start
# 访问
http://localhost:8080
# 目录介绍
# static 静态文件
# .babelrc es6解析
# .postcssrc.js css配置
3.语法
main.js中创建vue实例
// 创建实例
new Vue({
// id
el: '#app',
// 组件
components: { App },
// 模板
template: '<App/>'
})
HelloWorld.vue
<template>
<div class="hello">
<!--模版语法{{}} 只能存在单行语句-->
<!--{{ '哈哈' }}-->
<!--{{ 20 + 1 }}-->
<!--{{ "OK" ? "yes" : "no" }}-->
<!--{{ "hello".split("").reverse().join("") }}-->
<!--{{ msg }}-->
<!--只渲染一次-->
<!--<span v-once>{{ msg }}</span>-->
<!--<div v-html="hello"></div>-->
<!--绑定属性url-->
<!--<a v-bind:href="url">{{ url_name }}</a>-->
<!--<div v-bind:class="divClass">容器</div>-->
<!--简写-->
<!--<div :class="divClass">容器</div>-->
<!--属性可以写表达式-->
<!--<div :class="div2Class+'-1'"></div>-->
<!--条件渲染-->
<div v-if="flag">孙悟空</div>
<div v-else>六耳猕猴</div>
<!--<div v-show="flag">真*三国无双</div>-->
<!-- <ul>
<li v-for="name in names">{{ name }}</li>
</ul>-->
<!-- 遍历复杂对象 -->
<!-- <ul>
<li v-for="name in names">{{ name.name }}{{ name.age }}</li>
</ul>-->
<!--下标 :key-->
<!-- <ul>
<li v-for="(name, index) in names" :key="index">{{index}}--{{ name.name }}--{{ name.age }}</li>
</ul>-->
<!--遍历对象-->
<!-- <ul>
<li v-for="(value, key, index) in obj">{{value}}-{{key}}-{{index}}</li>
</ul>-->
<!--事件 传递参数-->
<button @click="clickHandler('hahaha', $event)">按钮</button>
<ul>
<li v-for="item in helloArr">{{ item }}</li>
</ul>
<!-- 点击按钮添加数组数据 -->
<button @click="addHandler">添加数据</button>
</div>
</template>
<script>
export default {
// v-bind 绑定属性
// v-if 条件渲染
// v-show 条件渲染,效果与单一v-if相同
// v-if 确保子组件销毁重建,高开销
// v-show总是渲染,通过css控制隐藏
// v-for 列表渲染
// 遍历推荐加key
// 事件
name: 'HelloWorld',
methods:{
clickHandler(data) {
// 接收参数
console.log(data);
console.log(event);
// 点击按钮改变data数据
// 引起视图更新
// this.flag = true;
this.flag = !this.flag;
},
addHandler(event) {
// 注意数组方法是改变原数组还是创建新数组
// 变异方法,改变原数组,引起视图变化 push pop...
// this.helloArr.push("hello4");
// 创建新数组,不引起视图变化 filter slice concat
this.helloArr.concat(["hello4", "hello5"]);
}
},
// 相当于react的state
data () {
return {
msg: 'Hello Vue',
hello: "<h3>Hello H3</h3>",
url_name: "百度",
url: "https://www.baidu.com",
divClass: "isActive",
div2Class: "list",
flag: false,
/* names: [
"张三",
"李四",
"王五"
],*/
names: [
{
name: "zhangsan",
age: 18
},
{
name: "lisi",
age: 19
}
],
obj: {
name: "vincent",
age: 18
},
helloArr: ["hello1", 'hello2', "hello3"]
}
}
}
</script>
<style scoped>
</style>
计算属性 class与style绑定 v-model
<template>
<div id="VueDemo">
<!--重复表达式-->
<!--{{ getMsg }}-->
<!--{{ mMsg() }}-->
<!--<div>-->
<!--{{ getMsg }}-->
<!--{{ mMsg() }}-->
<!--</div>-->
<!--根据值判断key是否存在-->
<!--静态class不会被替代-->
<!--<div class="old" :class="{'active':classDemo, 'txt': classDemo}">-->
<!--css-->
<!--</div>-->
<!--<div :class="{'c1':c1, 'c2':c2}">test</div>-->
<!--<div :class="cssObj">test</div>-->
<!--通过事件改变css-->
<!--<button @click="changCss">改变css</button>-->
<!--属性数组-->
<!-- <div :class="[activeClass, errorClass]" >
test1
</div>-->
<!--属性数组 三目表达式-->
<!--<div :class="[isActive ? activeClass : '', errorClass]">test2</div>-->
<!--复杂属性表达式-->
<!--<div class="old" :class="[isActive ? 'active' : 'noActive', {'text': classDemo}]">test3</div>-->
<!--属性还可以拼接字符串 对象key不能拼接字符串-->
<!--<div class="old" :class="[isActive ? 'active' : helloC+'noActive', {'text': classDemo}]">test3</div>-->
<!--内联样式-->
<!-- <div :style="{color:'red', fontSize:'40px'}">
style
</div>-->
<!--v-model双向数据绑定,忽略value-->
<!-- <div class="myInput">
<input v-model="inputMsg" type="text">
<p>{{ inputMsg }}</p>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
</div>-->
<!--修饰符-->
<!--
lazy 回车或失去焦点才会绑定
number 只接收数字类型
trim 去掉前后空格
-->
<!--<input v-model.lazy="inputMsg" type="text">-->
<!--<input v-model.trim="inputMsg" type="text">-->
<p>{{ inputMsg }}</p>
<button @click="changeName">改变myname</button>
</div>
</template>
<script>
export default {
name: "VueDemo",
data() {
return {
msg: "hello",
classDemo: true,
// c1: true,
// c2: true,
cssObj: {
'c1': true,
'c2': true
},
activeClass: 'active',
errorClass: 'text-danger',
isActive: false,
helloC: 'he_',
inputMsg: '',
checked: true,
myname: 'vincent'
}
},
// 实时监听数据变化
// 计算属性vs watch 不要滥用watch
watch: {
inputMsg(data) {
if(data == '100') {
console.log(100);
}
},
myname(data) {
console.log(data)
}
},
// 计算属性
// 计算属性根据依赖进行缓存
computed: {
getMsg() {
// 如果数据不改变不会重新计算
return this.msg.split('').reverse().join('')
}
},
methods: {
// 函数每次调用都会执行
mMsg() {
return this.msg.split('').reverse().join('');
},
changCss() {
this.cssObj = {
c1: false,
c2: true
}
},
changeName() {
this.myname += "1";
}
}
}
</script>
<style scoped>
.active {
color: red;
}
.txt {
font-size: 30px;
}
</style>
3.组件
A.vue
<template>
<div>
组件A: {{ msg }}
<!--切换组件后组件消失-->
<button @click="change">change</button>
</div>
</template>
<script>
export default {
name: "A",
data() {
return {
msg: "默认"
}
},
methods: {
change() {
this.msg = "改变";
}
}
}
</script>
<style scoped>
</style>
B.vue
<template>
<div>
组件B
</div>
</template>
<script>
export default {
name: "B"
}
</script>
<style scoped>
</style>
Learn.vue
<template>
<!--只能存在一个根容器-->
<div class="container">
新的组件: {{ title }}
<button @click="sendMsg">向父组件传递数据</button>
<input v-model="searchText" type="text">
</div>
</template>
<script>
export default {
name: "Learn",
// 所有初始化状态放入data
data() {
return {
searchText: ""
}
},
// 接收父组件传递的数据
props: ["title"],
methods: {
sendMsg() {
// this.$emit("getMsg", "我是儿子的数据");
this.$emit("getMsg", this.searchText);
}
}
}
</script>
<!--样式 scoped限制css只作用于当前组件-->
<style lang="css" scoped>
.container {
background: red;
}
</style>
App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<!--<HelloWorld/>-->
<!--<VueDemo />-->
<!--组件传递数据-->
<!--<Learn title="learn vue"/>-->
<!-- <input v-model="parentText" type="text">
<Learn @getMsg="getSonMsg" :title="parentText"/>
{{ demo }}-->
<!-- <A />
<B />-->
<!-- 组件不重复渲染以保存状态 -->
<keep-alive>
<component v-bind:is="currentComponent"></component>
</keep-alive>
<button @click="changeComponent">切换组件</button>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld'
import VueDemo from './components/VueDemo'
// 引入
import Learn from './components/Learn'
import A from './components/A'
import B from './components/B'
export default {
name: 'App',
data() {
return {
demo: "",
parentText: "",
title: {
name: "vincent"
},
currentComponent: A,
flag: false
}
},
components: {
// HelloWorld,
// VueDemo,
// 注入
Learn,
A,
B
},
methods: {
getSonMsg(data) {
this.demo = data;
},
changeComponent() {
if(this.flag) {
this.flag = false;
this.currentComponent = A;
} else {
this.flag = true;
this.currentComponent = B;
}
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
4.动画 | 自定义指令
<template>
<div class="anim">
<button @click="show = !show">toggle</button>
<!-- <transition name="fade">
<p v-if="show">hello</p>
</transition>-->
<!--自定义名字-->
<!-- <transition name="vincent">
<!–<p v-if="show">hello</p>–>
<p class="box" v-if="show"></p>
</transition>-->
<transition
name="custom-classes-transition"
enter-active-class="animated rollIn"
leave-active-class="animated zoomOutDown"
>
<p class="box" v-if="show">hello</p>
</transition>
<input v-focus type="text">
<p v-myStyle>哈哈哈</p>
</div>
</template>
<script>
// 动画
export default {
name: "Anim",
data() {
return {
show: true
}
},
// 局部指令
// 很多位置实现相同效果时用指令实现
directives: {
focus: {
inserted: function (el) {
el.focus();
}
},
myStyle: {
inserted: function (el) {
el.style.fontSize = "40px";
}
}
}
}
</script>
<style scoped>
/* .fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}*/
/* .vincent-enter, .vincent-leave-to {
opacity: 0;
}
.vincent-leave-to, .vincent-enter {
opacity: 1;
}
.vincent-enter-active, .vincent-leave-active {
transition: opacity .5s;
}*/
/* 过渡效果 */
/* .vincent-enter, .vincent-leave-to {
opacity: 0;
width: 0;
}
.vincent-enter-to, .vincent-leave {
opacity: 1;
width: 100px;
}
.vincent-enter-active, .vincent-leave-active {
transition: all 1s;
}
.box {
width: 100px;
height: 100px;
background: red;
}*/
/* .vincent-enter, .vincent-leave-to {
}
.vincent-enter-to, .vincent-leave {
}
.vincent-enter-active {
animation: bounce-in 1s;
}
.vincent-leave-active {
animation: bounce-out 1s;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
@keyframes bounce-out {
0% {
transform: scale(1);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(0);
}
}*/
.box {
width: 100px;
height: 100px;
background: red;
}
</style>
5.过滤器
Child.vue
<template>
<div>
{{ money | rmb }}<br>
{{ txt | author }}
</div>
</template>
<script>
export default {
name: "FilterDemo",
data() {
return {
money: 20,
txt: "To be or not to be"
}
},
// 过滤器对变量格式化处理
filters: {
rmb: function (value) {
if(!value) return;
value = value.toString();
return "$" + value;
},
author: function (value) {
if(!value) return;
return value + "--by vincent";
}
}
}
</script>
<style scoped>
</style>
6.父子组件 | 插槽
<template>
<div class="child">
{{ getFoo }}
子组件: {{ title }}--{{ age }}--{{ nick }}--{{ parent }}
<ul>
<li v-for="g in girlFriend">{{ g }}</li>
</ul>
<button @click="sendMonney">赚了</button>
</div>
</template>
<script>
// 子组件可以读取父组件中的元素,但不建议,破坏耦合性
// $root全局变量也不建议
export default {
name: "Child",
data() {
return {
}
},
methods: {
sendMonney() {
this.$emit("money", "翻了5倍");
}
},
computed: {
getFoo() {
return this.$root.foo;
}
},
// props: ["title", "age"]
// 指定类型
props: {
title: String,
age: Number,
// 默认值
nick: {
type: String,
default: "国民儿子"
},
parent: {
type: String,
required: true
},
// 默认值如果是数组或对象必须返回function
girlFriend: {
type: Array,
default: function () {
return ["凤姐", "芙蓉姐姐"]
}
}
}
}
</script>
<style scoped>
</style>
Parent.vue
<template>
<div class="parent">
<slot ct="我是parent的数据">
</slot>
父组件: {{ money }}
<Child @money="getMoney" parent="王健林" nick="娱乐圈键盘侠" title="一个亿的小目标" :age="age"/>
</div>
</template>
<script>
import Child from './Child'
import SlotDemo from './SlotDemo'
export default {
name: "Parent",
data() {
return {
money: "",
age: 30
}
},
components: {
Child, SlotDemo
},
methods: {
getMoney(data) {
this.money = data;
}
}
}
</script>
<style scoped>
</style>
SlotDemo.vue
<template>
<!-- <div class="slotDemo">
<slot></slot>
</div>-->
<div class="slotDemo">
<!-- <slot name="v1"></slot>
<slot name="v2">
我是插槽默认信息
</slot>-->
<!--儿子给父亲传数据-->
<slot ct="我是儿子的数据">
</slot>
</div>
</template>
<script>
// 插槽,内容分发,传递视图
// 具名插槽 可以传递多个插槽
// 插槽可以设置默认信息
// 插槽可以设置动态内容,注意作用域
// 插槽的视图是父组件传递的
// 视图要显示的内容由子组件决定
// 效果由父组件决定,数据由子组件决定
export default {
name: "SlotDemo",
}
</script>
<style scoped>
</style>
7.音乐 | 操作原生DOM
<template>
<div class="music">
音乐实例
<p class="p1" ref="p1">原生P元素</p>
<input type="text" ref="myinput">
<button @click="getValue">按钮</button>
</div>
</template>
<script>
// 操作原生dom
// 改变内容和属性可以通过绑定数据
// input标签 audio标签需要通过原生dom操作
// 没有必要不要操作原生dom,性能不如虚拟dom
export default {
name: "Music",
data() {
return {
}
},
mounted() {
this.$refs.p1.innerHTML = "改变吧"
this.$refs.myinput.value = "哈哈"
console.log(this.$refs.p1)
},
methods: {
getValue() {
console.log(this.$refs.myinput.value)
}
}
}
</script>
<style scoped>
</style>
8.网络请求
vue-resource不维护,推荐axios
axios基于promise
中文文档
https://www.kancloud.cn/yunye/axios/234845
# 安装
npm install --save axios
# main.js引入
import Axios from "axios"
Vue.prototype.$axios = Axios
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 Axios from "axios"
Vue.prototype.$axios = Axios
Vue.config.productionTip = false
// 全局配置
Axios.defaults.baseURL = 'http://localhost';
Axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
// 拦截器
// 添加请求拦截器
Axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
if(config.method === "post"){
config.data = qs.stringify(config.data)
}
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
Axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
if(!response.data){
return {
msg:"数据返回不合理"
}
}
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
// 自定义全局指令
Vue.directive('focus', {
// 常用
bind:function() {
console.log("只调用一次")
},
// 常用
// 当前指令生命周期
// 插入到元素当中时执行
inserted:function (el, binding) {
console.log(binding);
console.log(el);
el.focus();
},
update:function () {
console.log("更新")
}
})
/* eslint-disable no-new */
new Vue({
el: '#app',
// 全局变量
data:{foo: "hellofoo"},
components: { App },
template: '<App/>'
})
跨域问题
使用代理解决
config/index.js
// 跨域处理
proxyTable: {
'/dfun_api':{
target: 'http://localhost',
pathRewrite: {
'^/dfun_api': ''
},
changeOrigin: true
}
},
main.js
注意注掉之前的全局配置
// 跨域处理
Vue.prototype.HOST = "/dfun_api"
请求
var url = this.HOST + "/04_php/basic/b1_ajax.php";
// post请求, 跨域
this.$axios.post(url, qs.stringify({
...
修改配置文件需要重启服务器
9 路由
# 创建项目
vue init webpack 03-router
cd 03-router
npm install
# 安装路由
npm install vue-router --save
# main.js
# 引入路由,创建路由,注入实例
------
import VueRouter from 'vue-router'
vue.use(VueRouter)
// 创建路由
const router = new VueRouter({
routes: [
{
path: '/hello',
name: 'HelloWorld',
component: HelloWorld
}
]
new Vue({
el: '#app',
// 注入实例对象
router,
components: { App },
template: '<App/>'
})
------
# 显示路由 App.vue
------
<div id="app">
<!--显示路由-->
<router-view />
</div>
------
路由嵌套
编程式导航
gotoHello() {
// this.$router.push("/hello")
// 传递对象
/* this.$router.push({
'path': '/hello'
})*/
// replace方式,不会向history添加记录
this.$router.replace({path: 'hello'})
// 回退到之前一个页面
this.$router.go(-1)
}
10 element-ui
https://element.eleme.cn/#/zh-CN/component
https://iviewui.com/docs/introduce
npm i element-ui -S
npm install babel-plugin-component -D
11 swipper组件
https://github.com/surmon-china/vue-awesome-swiper
https://www.swiper.com.cn/
npm install swiper vue-awesome-swiper --save
npm install --save axios
11 组件库
https://github.com/vuejs/awesome-vue
使用见组件文档
npm install --save vue-awesome-swiper
npm install --save pull-to-refresh