vue
传统开发模式 | 现代开发模式 |
---|---|
jQuery | vue/react |
80%的精力放在表现层 | 20%的精力放在表现层 |
前端渲染 | 后台渲染 |
---|---|
降低服务器负担、带宽压力小、用户体验好 | SEO、兼容、安全性 |
vue的核心是数据
- {{}}
<body>
<div id="div1">
{{name}}
{{age}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#div1',
data: {
age: 18,
name: 'zhangsan'
}
})
</script>
</body>
{{}}双括号里面的内容vue会到data里面去找。但是缺点是页面加载延迟的时候会出现{{name}}的状态
./1.php
<?php
sleep(3)
?>
sleep(3)
./index.html
<body>
<div id="div1">
{{name}}
{{age}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#div1',
data: {
age: 18,
name: 'zhangsan'
}
})
</script>
</body>
扔到服务器去访问
三秒前
三秒后
v-clock
<style>
*[v-clock] {display: none}
</style>
指令
补充了html的属性
v-bind
v-bind:绑定数据,用于任何属性
<div id="div1">
<p v-bind:title="age">{{name}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el:'#div1',
data: {
age: 18,
name: 'zhangsan'
}
})
</script>
v-bind:xxx
:xxx(缩写)
<p :title="age">{{name}}</p>
两个属性有另外的写法
(1)class="aa bb"
.aa {
width: 200px;
height: 200px
}
.bb {
background-color: red
}
<div id="div1">
<div :class="class_str">1</div>
<hr>
<div :class="class_arr">2</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#div1',
data: {
class_str: 'aa bb',
class_arr: ['aa', 'bb']
}
})
</script>
(2)style="width: 200px;backgound-color: red"
<div id="div1">
<p :style="style_str">{{content}}</p>
<strong :style='style_json'>{{content}}</strong>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#div1',
data: {
content: '我是中国人',
style_str: 'color: red; fontSize: 40px',
style_json: {color:'green',fontSize:'20px'}
}
})
</script>
v-mode
双向绑定
<div id='div1'>
<input v-model="name" type='text'/>
<p>{{name}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#div1',
data: {
name: 'lwp'
}
})
你输入啥就立马显示啥
事件
v-on:click=""||@click=""
<body>
<div id="div1">
<input type="button" @click="fn()">
</div>
</body>
<script>
let vm = new Vue({
el: '#div1',
methods: {
fn() {
alert('1')
}
}
});
</script>
事件修饰符
事件冒泡
<body>
<div id="div1" @click="father()">
<input type="button" @click="son()">
</div>
</body>
<script>
let vm = new Vue({
el: '#div1',
methods: {
father() {
alert('father')
},
son() {
alert('son')
}
}
});
</script>
.stop阻止事件冒泡
<body>
<div id="div1" @click="father()">
<input type="button" @click.stop="son()">
</div>
</body>
<script>
let vm = new Vue({
el: '#div1',
methods: {
father() {
alert('father')
},
son() {
alert('son')
}
}
});
</script>
修饰符 | 作用 |
---|---|
prevent | 阻止默认事件 |
stop | 阻止事件冒泡 |
native | 原生(配合组件使用) |
keycode|name | 按键 |
once | 只会一次操作,后面就失效了 |
self | 只认自己 |
capture | 捕获阶段的事件 |
key:连着用,多个按键同时按才触发
.ctrl.enter
computed
<body>
<div id="div1">
{{num1}}+{{num2}}={{num3}}
</div>
</body>
<script>
let vm = new Vue({
el: '#div1',
data: {
num1: 1,
num2: 1,
},
computed: {
num3() {
return this.num1 + this.num2
},
}
});
</script>
这个功能用methods也能实现
但是computed有好处
1、缓存(跟methods每次计算不一样,只有数据变才会重新计算)->性能高
2、方便:既能读又能写
既能读又能写
<body>
<div id="div1">
<input type="text" v-model="familyName">
<input type="text" v-model="givenName">
<input type="text" v-model="name">
</div>
</body>
<script>
let vm = new Vue({
el: '#div1',
data: {
familyName: '张',
givenName: '三',
},
computed: {
name: {
get() {
return this.familyName + this.givenName
},
set(value) {
this.familyName = value[0];
this.givenName = value.substring(1)
}
},
}
});
</script>
既能从前到后
也能从后到前
高亮表示输入
监听
<body>
<div id="div1">
<input type="text" v-model="name">
</div>
</body>
<script>
let vm = new Vue({
el: '#div1',
data: {
name: 'lwp'
},
watch: {
name() {
console.log('name change')
}
}
});
</script>
数据通信
axios
get
前面我们都是引用vue.js的做法,这种方式太low了,接下来我们用webpack的方式,以后也都用这种方式
npm init -y
npm i axios vue -D
./webpack.config.js
const path = require('path')
module.exports={
mode:'development',
entry:'./src/vm.js',
output:{
path:path.resolve(__dirname,'build'),
filename:'bundle.min.js'
}
}
./src/vm.js
import Vue from 'vue/dist/vue.esm'
import Axios from 'axios'
let vm=new Vue({
el:'#div1',
data:{
name:'',
age:''
},
async created(){
let {data}=await Axios.get('./data/user.json');
this.name=data.name;
this.age=data.age
},
template:'<div>姓名:{{name}}年龄:{{age}}</div>'
})
./data/user.json
{
"name":"lwp",
"age":18
}
./index.html
<body>
<div id="div1"></div>
</body>
<script src="./build/bundle.min.js"></script>
把代码扔到服务器去
webpack编译一下
post
./src/vm.js
import Vue from 'vue/dist/vue.esm'
import Axios from 'axios'
import {stringify} from 'querystring'
const axios=Axios.create({
transformRequest:[
function(data){
stringify(data)
}
]
})
let vm=new Vue({
el:'#div1',
data:{
sum:0
},
async created(){
let {data}=await axiost({
url:'./data/sum.php',
data:{a:1,b:1}
});
this.sum=data
},
template:'<div>{{sum}}</div>'
})
./data/sum.php
<?php
echo $_POST('a')+$_POST('b')
?>
./index.html
<body>
<div id="div1"></div>
</body>
<script src="./build/bundle.min.js"></script>
把代码扔到服务器去
webpack编译一下
fetch
get
./src/vm.js
import Vue from 'vue/dist/vue.esm'
let vm=new Vue({
el:'#div1',
data:{
name:'',
age:''
},
async created(){
let res=await fetch('./data/user.json');
let data=await res.json();
this.name=data.name;
this.age=data.age
},
template:'<div>姓名:{{name}}年龄:{{age}}</div>'
})
./data/user.json
{
"name":"lwp",
"age":18
}
./index.html
<body>
<div id="div1"></div>
</body>
<script src="./build/bundle.min.js"></script>
把代码扔到服务器去
webpack编译一下
post
./src/vm.js
import Vue from 'vue/dist/vue.esm'
let vm=new Vue({
el:'#div1',
data:{
sum:0
},
async created(){
let formdate= new FormData();
formdate.append('a',1)
formdate.append('b',1)
let res=await fetch('./data/sum.php',{
method: 'post',
body:formdate
});
this.sum=await res.json();
},
template:'<div>{{sum}}</div>'
})
./data/sum.php
<?php
echo $_POST('a')+$_POST('b')
?>
./index.html
<body>
<div id="div1"></div>
</body>
<script src="./build/bundle.min.js"></script>
把代码扔到服务器去
webpack编译一下
组件
局部
./src/vm.js
import Vue from 'vue/dist/vue.esm'
let vue = new Vue({
el: '#div1',
data: {},
template: '<div><cmp1/></div>',
components: {
cmp1: {
data() {
return { a: 12 }
},
template: '<div>{{a}}</div>'
}
}
})
全局
刚刚正在一块,我们现在把components那块分出去
传参
./src/vm.js
import Vue from 'vue/dist/vue.esm'
import './cmp1'
let vue = new Vue({
el: '#div1',
data: {},
template: '<div><cmp1 name="lwp"/></div>',//传过去
})
./src/cmp1.js
import Vue from 'vue/dist/vue.esm'
Vue.component('cmp1', {
props: ['name'],//接收
data() {
return { a: 22 }
},
template: '<div>{{name}}</div>'
})
但是有一个问题,如果vm.js想传数字,因为传到cmp1.js的是字符串,再用parseInt太麻烦了,因此数字是这么传的,加个冒号
./src/vm.js
import Vue from 'vue/dist/vue.esm'
import './cmp1'
let vue = new Vue({
el: '#div1',
data: {},
template: '<div><cmp1 name="lwp" :age:"12"/></div>',//传过去
})
动态组件
先提取出来
./src/vm.js
import Vue from 'vue/dist/vue.esm'
import './cmp1'
let vue = new Vue({
el: '#div1',
data: {},
template: '<div><component is="cmp1" name="lwp" :age="24"/></div>',
})
./src/vm.js
import Vue from 'vue/dist/vue.esm'
import './cmp1'
let vue = new Vue({
el: '#div1',
data: {},
template: '<div><component is="cmp1" name="lwp" :age="24"/></div>',
})
单独实例化
组件如果不想单单在前台渲染而已,想做例如测试,就必须能够new出来
./src/cmp1.js
import Vue from 'vue/dist/vue.esm'
export default Vue.component('cmp1', {
props: ['name', 'age'],
data() {
return { a: 11 }
},
template: `<div>
{{name}}
<p>{{age+1}}</p>
</div>`
})
./src/vm.js
import Cmp1 from './cmp1'
let cmp = new Cmp1({
propsData: {
name: '张三',
age: 12
}
})
let vm = cmp.$mount();
console.log(vm.$el);
if (vm.$el.querySelector('p').innerHTML == '13') {
console.log('正确')
} else {
console.log('失败')
}
自定义组件
cnpm i bootstrap@3 -D
./src/vm.js
import MyDialog from './my-dialog'
template:`
<div>
<my-dialog>
<template slot="title">标题</template>
内容
</my-dialog>
</div>
`
./src/my-dialog.js
imort Vue from 'vue/dist/vue.esm'
import from 'bootstrap/dist/css/bootstrap.css'
import from './my-dialog.css'
export default Vue.component('my-dialog',{
template:`
<div class="panel panel-default my-dialog">
<div class="panel-heading">
<slot name="title"/>
</div>
<div class="panel-body">
<slot/>
</div>
</div>
`
})
./src/css/my-dialog.css
.my-dialog.css {
width: 400px;
height: 160px;
border: 1px solid #ccc;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
./webpack.config.js
module: {
rules: [
{test:/\.css$/,use:['style-loader','css-loader']},
{test:/(eot|svg|ttf|woff|woff2)$/,use:'file-loader'}
]
}
重构一下
之前的做法存在一些不方便的问题,通过重构改进一下,可以当成是模板
1.之前得手动在index.html 引入./dest/bundle.min.js表示生产模式,而引入bundle.min.js表示开发模式,使用dev-server,而且还得在浏览器去输入地址
- --open 可以npm run start完直接打开浏览器访问localhost:8080
"start": "webpack-dev-server --open"
2.之前得import 'vue/dist/vue.esm'
resolve: {
alias: {
'vue': 'vue/dist/vue.esm',
'bootstrap': 'bootstrap/dist/css/bootstrap.css'
}
},
有了这个,引入vue就行
代码下载
组件间通信
土办法
组件事件
on 接收
once 一次
父子间通信
非父子间通信十分复杂,这里不做介绍,一般也不会用这种办法
./src/component/father.js
import Vue from 'vue'
import Son from './son'
export defaut Vue.component('father',{
template:`
<div>
<div>父级</div>
</son>
</div>
`
})
./src/component/son.js
import Vue from 'vue'
export defaut Vue.component('son',{
template:`
<div>
<div>子级</div>
</div>
`
})
./src/index.js
import Vue from 'vue'
import Father from './component/father'
let vue = new Vue({
el:'#div1',
template: `
</father>
`
})
父亲找儿子
./src/component/father.js
import Vue from 'vue'
import Son from './son'
export defaut Vue.component('father',{
methods:{
fn(){
this.$refs.c1.num++;
}
},
template:`
<div>
<div">
父级
<input type="button" value="+1" @click="fn()" />
</div>
<son res="c1"/>
</div>
`
})
./src/component/son.js
import Vue from 'vue'
export defaut Vue.component('son',{
data(){
return{
num: 0
}
},
template:`
<div>
<div>子级</div>
</div>
`
})
儿子找父亲
./src/component/father.js
import Vue from 'vue'
import Son from './son'
export defaut Vue.component('father',{
data() {
return {num: 0}
},
methods:{
fn(){
this.num++;
}
},
template:`
<div>
<div">
父级:{{num}}
</div>
<son :parent="this"/>
</div>
`
})
./src/component/son.js
import Vue from 'vue'
export defaut Vue.component('son',{
props: ['parent'],
methods: {
this.parent.fn();
},
template:`
<div>
子级
<input type="button" value="+1" @click="fn()" />
</div>
`
})
emitAndOn
./src/component/father.js
import Vue from 'vue'
import Son from './son'
export default Vue.component('father', {
methods: {
fn() {
this.$refs.c1.$emit('add_num', 7);
}
},
template: `
<div>
<div>
父级
<input type="button" value="+7" @click="fn()"/>
</div>
<son ref="c1"/>
</div>
`
})
./src/component/son.js
import Vue from 'vue'
export default Vue.component('son', {
data() {
return {
num: 0
}
},
template: `
<div>子级:{{num}}</div>
`,
created() {
this.$on('add_num', function(n) {
this.num += n;
})
}
})
Vue2.0的写法
Vue2.0和Vue1.0的写法主要在组件的写法,会更简单,最后也会编译成Vue1.0的那种代码
./src/component/1.vue
<template lang="html>
</template>
<script>
export default {
name:'xx',//这个名字是调试使用
data() {},
methods: {}
}
</script>
<style></style scoped>//没有scope表示里面的样式是全局,有是局部,当前页面有效
使用这个插件的时候报错会报name,利于寻找
cnpm i vue-loader vue-style-loader vue-html-loader vue-template-compiler -D
./webpack.config.js
{test:/\.css/,use:['vue-style-loader','css-loader']},
{test:/\.vue/,use:'vue-loader'}
./config/webpack.development.js
const VueLoaderPlugin=require('vue-loader/lib/plugin')
const HtmlWebpackPlugin=require('html-webpack-plugin')
plugins:[
new VueLoaderPlugin()
]
./src/index.js
import Vue from 'vue'
import Cmp1 from './cpmponent/cmp1.vue'
let vue = new Vue ({
el: '#div1',
cmponents: {
Cmp1//注册一下
},
template: `
<div><cmp1/></div>
`
})
注册一下
组件套组件
less
cnpm less less-loader -D
{test:/.less$/,use:['vue-style-loader','css-loader','less-loader']}
之后组件的任何写法和前面vue1.0的写法没有任何区别
正经的写法
cnpm i vue-router -S
数据交互
1.aiox
cnpm i axios vue-axios -S
2.vue-resource
3.fetch
cnpm i vue-resource -S
S是生产模式 D是开发模式
./src/index.js
import Vue from 'vue'
import App from './app.vue'
import router from './routers'
// import Axios from 'axios'
// import VueAxios from 'vue-axios'
// Vue.use(VueAxios, Axios);
// import VueResource from 'vue-resource'
// Vue.use(VueResource)
let vue = new Vue({
el: '#div1',
data: {},
components: {
App
},
router,
template: `
<App/>
`
})
./src/components/index.vue
<template lang="html">
<div >首页</div>
</template>
<script>
import { constants } from 'crypto';
export default {
name:'index',
async created(){
// let {data} = await this.axios.get('http://localhost:8080/data/1.json');
// let {data} = await this.$http.get('http://localhost:8080/data/1.json')
let res=await fetch('http://localhost:8080/data/1.json')
let data=await res.json();
console.log(data)
}
}
</script>
vue-cli(启动器)
脚手架
安装npm i vue-cli -g
vue
如果提示“无法识别 'vue' ” ,有可能是 npm 版本过低,可以使用 npm install -g npm 来更新版本
vue init 初始化(常用)
vue list 模板(常用)//其他的到github去搜vue template
vue list
使用webpack
vue init webpack project1
前面都选第一个,这里选第三个,因为我们要使用cnpm
vue-cli目录介绍
路由
路由拦截
全局
只要没登陆,所有的页面就看不见
//只要localStorage没有存token或token过期,就会让用户登陆
./src/App.vue
if (!localStorage.token) {
this.$router.push("/login");
}
在登陆成功后,把后台获取到的token用localStorage存起来,并跳到首页
localStorage.token=json.token;
this.$router.push('/index')
局部
有些页面没登陆就不让你看
main.js
router.beforeEach((to,from,next)=>{
if(to.meta.requireAuth){
if(localStorage.token){
next()
}else{
next({
path:'/login',
query:{redirect:to.fullPath}
})
}
}else{
next()
}
})
router.js
meta:{
requireAuth:true
},
有需要登陆的路由就使用这个属性
vuex
cnpm i vuex -S
功能
1.数据跨组件共享
2.防止数据意外修改
3.方便调试
state:数据
mutations:修改state的唯一渠道,在其他地方修改state会报错,目的就是为了追踪哪个mutation修改了state,修改了哪些地方
devtools监控mutaitions
action:mutations的封装
state修改,vue components(组件)里面的数据也会改
vue components不能直接操作mutation,而必须调用actions,让actions去调用mutation
action可以异步操作,mutations不能
backend api 后台数据
./src/main.js
//1.引入
import vuex from 'vuex'
//2.挂载
//2.1vuex放在vue身上
Vue.use(Vuex)
//3.声明store对象
const store = new Vuex.Store({
strict:process.env.NODE_ENV!='production',
getters:{},
state:{},
mutations:{},
modules:{},
actions:{}
})
//2.2store放在vue身上
new Vue({
store,
})
strict(true严格模式)在开发阶段设为true,发布阶段设为false,因为strict的实现原理是深度比较做检查,当state一多会非常慢
vuex辅助方法
映射
mapState state -> computed
mapActions actions -> methods
mapGetters getters -> computed
代码下载
动画
vue init webpack animate
在config/index.js26useEslint改成false
cnpm start
cnpm i vue2-animate -D
这种显示隐藏十分僵硬
用于一个单个对象
用<transition>标签包起来,name设为fade,fadeDown等其他名字就有不同效果,前提的有个css样式:时间:animate-duration:1s
./src/components/index.vue
<template lang="html">
<div>
<input type="button" value="显示隐藏" @click="b=!b">
<transition name="fadeDown">
<div id="box" v-if="b">fade
</div>
</transition>
</div>
</template>
<script>
export default {
name:'Index',
data(){
return {b:true}
},
}
</script>
<style lang="css" scoped>
#box {
width: 100px;
height: 100px;
background-color: #ccc;
animation-duration: 1s
}
</style>
一组
用transition-group包起来,tag设置为我们想要的标签,必须得有key,建议是id,不能使用index
<template lang="html">
<div>
<transition-group name="fadeUp" tag="ul">
<li v-for="item in arr" :key="item">
{{item}}
<a href="#" @click="del(index)">delete</a>
</li>
</transition-group>
</div>
</template>
<script>
export default {
name: "Index",
data() {
return {
b: true,
arr: [1, 5, 55, 6, 25, 2, 8, 75, 86, 95]
};
},
methods: {
del(index) {
this.arr.splice(index, 1);
}
}
};
</script>
<style lang="css" scoped>
ul li {
width: 100px;
height: 100px;
background-color: #ccc;
animation-duration: 1s;
margin: 10px;
}
</style>
vue项目-pc端
https://www.jianshu.com/p/35c9f952a792