前 15 章 均使用 1.0 写法
1. MVC 和 MVVM
2. 前台渲染和后台渲染
前台渲染 | 后台渲染 |
---|---|
降低服务器负担,带宽压力小 | SEO、兼容性 |
用户体验好 | 安全性 |
3. 模板渲染 {{}}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="./vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="div1">
姓名:{{name}} <br>
年龄:{{age}} <br>
年份:{{calcBirth()}}
</div>
</body>
<script>
let vm = new Vue({
el: '#div1',
data: {
name: 'ly',
age: 18
},
methods: {
calcBirth(){
return new Date('2000-01-19').getFullYear();
}
}
})
</script>
</html>
- 当我们数据发生改变时,会自动同步到视图。(核心)
4. 指令(directive)
v-bind
- v-bind 向HTML属性中添加值,可简写为
:
,如::title="age"
- 案例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./vue.js" type="text/javascript" charset="utf-8"></script> </head> <body> <div id="div1"> <strong v-bind:title="age">{{name}}</strong> </div> </body> <script> let vm = new Vue({ el: '#div1', data: { name: 'ly', age: 18 } }) </script> </html>
- 可以用于任何属性,有两个属性(class,style)有另外的写法
- 案例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./vue.js" type="text/javascript" charset="utf-8"></script> </head> <body> <div id="div1"> <!-- v-bind 指令 --> <strong v-bind:title="age" :class="class_str" :style="style_str">{{name}}</strong><br> <strong v-bind:title="age" :class="class_arr" :style="style_json">{{name}}</strong> </div> </body> <script> let vm = new Vue({ el: '#div1', data: { name: 'ly', age: 18, class_str: 'aa bb cc', class_arr: ['aaa', 'bbb', 'ccc'], style_str: 'display:block;with:200px;background:yellow', style_json: { display: 'block', background: 'green', width: '200px' } } }) </script> </html>
v-model 数据绑定数据双向绑定
- 数据绑定数据双向绑定
- 数据和 input 是双向绑定
- 跳过 controller——控制器 直接对话
- 只能用于输入组件
- 通过 v-model 的数据都是字符串
- 案例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./vue.js" type="text/javascript" charset="utf-8"></script> </head> <body> <div id="div1"> <input type="text" v-model="name"> <p>{{name}}</p> </div> </body> <script> let vm = new Vue({ el: '#div1', data: { name: 'ly' } }) </script> </html>
v-text
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="./vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="div1">
<div v-text='str'></div>
</div>
</body>
<script>
let vm = new Vue({
el: '#div1',
data: {
str:'Hello World'
}
})
</script>
</html>
v-html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="./vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="div1">
<div v-html='str'></div>
</div>
</body>
<script>
let vm = new Vue({
el: '#div1',
data: {
str:'<p>Hello World</p><p>Hello World</p><p>Hello World</p>'
}
})
</script>
</html>
v-on
- 给元素进行事件绑定;可简写成
@
,如:@click="fn()"
- 案例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./vue.js" type="text/javascript" charset="utf-8"></script> </head> <body> <div id="div1"> {{a}} <input type="button" value="+1" v-on:click="fn()"> </div> </body> <script> let vm = new Vue({ el: '#div1', data: { a:12 }, methods:{ fn(){ this.a++ } } }) </script> </html>
v-show 和 v-if
- v-show 控制 display
- v-if 删除这个元素
- 案例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./vue.js" type="text/javascript" charset="utf-8"></script> <style type="text/css"> .box { width: 200px; height: 200px; border: 1px solid red; } </style> </head> <body> <div id="div1"> <input type="button" value="显示隐藏" @click="fn()"> <div class="box" v-show="a"></div> <div class="box" v-if="a"></div> </div> </body> <script> let vm = new Vue({ el: '#div1', data: { a: true }, methods: { fn() { this.a = !this.a } } }) </script> </html>
v-for
- 虚拟dom
- 合并请求
- 快速查询
- 局部刷新
:key
属性- key的作用主要是为了高效的更新虚拟DOM。
- 不能重复
- 不能改变
- 案例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./vue.js" type="text/javascript" charset="utf-8"></script> </head> <body> <div id="div1"> <ul> <li v-for="user,index in user_arr" :key="user.id"> {{index}} 用户名: {{user.name}} 密码: {{user.password}} </li> <li></li> <li v-for="v,k in style_json"> {{k}}:{{v}} </li> <li></li> <li v-for="item,index in str"> {{index}}:{{item}} </li> <li></li> <li v-for="i in 3"> {{i}} </li> </ul> </div> </body> <script> let vm = new Vue({ el: '#div1', data: { user_arr: [{ id:'a1', name: 'ly', password: '123321' }, { id:'a2', name: '张三', password: '123321' }, { id:'a3', name: '李四', password: '123321' } ], style_json: { color: 'red', background: 'green' }, str:"abc" } }) </script> </html>
v-pre 预编译
- 提高性能
- 防止意外
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>预编译</title> <script src="./vue.js" type="text/javascript" charset="utf-8"></script> </head> <body> <div id="div1"> <div v-pre> {{a}}-{{b}} </div> </div> </body> <script> let vm = new Vue({ el: '#div1', data: { } }) </script> </html>
v-cloak
- 防止vue代码意外显示出来
- 编译完成前 v-cloak 会一直存在,完成后自动去除
- 案例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./vue.js" type="text/javascript" charset="utf-8"></script> <style type="text/css"> *[v-cloak]{ display: none; } </style> </head> <body> <div id="div1"> <div> {{a}}-{{b}} </div> <div v-cloak> {{a}}-{{b}} </div> </div> </body> <script> setTimeout(()=>{ let vm = new Vue({ el: '#div1', data: { a:1, b:2 } }) },3000) </script> </html>
5. 原生实现 vue 数据同步
- 源码:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <div id="div1"> <input type="text" v-model="name" /><br> 姓名:{{name}} <br> 年龄:{{age}} </div> </body> <script type="text/javascript"> let el = document.getElementById('div1'); // 保存原始 html let template = el.innerHTML; let _data = { name: 'ly', age: 18 }; let data = new Proxy(_data, { set(obj, name, value) { obj[name] = value; console.log('数据已发生改变'); render(); }, get() { } }); function render() { // 获取双花括号里面的变量 // 渲染 el.innerHTML = template.replace(/\{\{\w+\}\}/g, str => { str = str.substring(2, str.length - 2); return _data[str]; }); // 找所有的 v-model Array.from(el.getElementsByTagName('input')) .filter(ele => ele.getAttribute('v-model')) .forEach(input => { let name = input.getAttribute('v-model'); input.value = _data[name]; input.oninput = function() { data[name] = this.value; } }) } render(); </script> </html>
-----------------------------------
6. 事件修饰符
stop
阻止事件冒泡once
单次事件prevent
阻止默认事件native
原生事件(组件)keycode | name
可以直接筛选按键self
只处理自己的事件(冒泡的就不认)capture
捕获阶段的事件
7. computed —— 计算属性
-
带缓存,性能高 —— 当数据改变才会重新计算
-
方便 —— 可读、可写
-
案例一(读):
-
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./vue.js" type="text/javascript" charset="utf-8"></script> </head> <body> <div id="div1"> 姓: <input type="text" v-model="familyName"> 名: <input type="text" v-model="givenName"> {{name}} </div> </body> <script> let vm = new Vue({ el: '#div1', data: { familyName: '张', givenName: '三' }, computed: { name() { return this.familyName + ' ' + this.givenName; } } }) </script> </html>
-
案例二(读、写):
-
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./vue.js" type="text/javascript" charset="utf-8"></script> </head> <body> <div id="div1"> 姓: <input type="text" v-model="familyName"><br> 名: <input type="text" v-model="givenName"><br> <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> </html>
8. watch —— 监听属性
- 监听变量:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./vue.js" type="text/javascript" charset="utf-8"></script> </head> <body> <div id="div1"> <input type="text" v-model="name"> </div> </body> <script> let vm = new Vue({ el: '#div1', data: { name:'ly' }, watch:{ name(){ console.log('name已经改变'); } } }) </script> </html>
- 监听对象:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./vue.js" type="text/javascript" charset="utf-8"></script> </head> <body> <div id="div1"> <input type="text" v-model="obj.name"> </div> </body> <script> let vm = new Vue({ el: '#div1', data: { obj: { name: 'ly', age: 18 } }, watch: { 'obj.name': function() { console.log('obj name已经改变'); } } }) </script> </html>
9. vue-router
- 路由容器
- 声明路由表
- 将路由表添加到 vue
9.1 基本使用:
- 使用
router-link
选中时 自带router-link-exact-active router-link-active
两个类名<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="../vue.js" harset="utf-8"></script> <script src="../vue-router.js" charset="utf-8"></script> <style type="text/css"> .nav { color: gray; margin-right: 10px; } .router-link-active { color: black } </style> </head> <body> <div id="div1"> <router-link class="nav" to="/a">页面a</router-link> <router-link class="nav" to="/b">页面b</router-link> <br> 文字 <!-- 路由容器 --> <router-view></router-view> </div> </body> <script> // 路由表 let router = new VueRouter({ routes: [{ path: '/a', component: { template: '<div>/a</div>' } }, { path: '/b', component: { template: '<div>/b</div>' } }] }); let vm = new Vue({ el: '#div1', data: {}, router }) </script> </html>
9.2 命名路由(name 可选)
- 当路由级别比较深时方便我们找到对应的路由
- 路由跳转时使用
:to="{name:'路由对应的name'}"
进行跳转,M=
如:<router-link class="nav" :to="{name:'a'}">页面a</router-link>
- 案例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="../vue.js" harset="utf-8"></script> <script src="../vue-router.js" charset="utf-8"></script> <style type="text/css"> .nav { color: gray; margin-right: 10px; } .router-link-active { color: black } </style> </head> <body> <div id="div1"> <router-link class="nav" :to="{name:'a'}">页面a</router-link> <router-link class="nav" :to="{name:'b'}">页面b</router-link> <br> 文字 <!-- 路由容器 --> <router-view></router-view> </div> </body> <script> // 路由表 let router = new VueRouter({ routes: [{ path: '/a', name:'a', component: { template: '<div>/a</div>' } }, { path: '/b', name:'b', component: { template: '<div>/b</div>' } }] }); let vm = new Vue({ el: '#div1', data: {}, router }) </script> </html>
9.3 路由传参
$route
当前路由的信息- 使用
$route.params
可以获取到传递过来的信息 - 可以使用以下方式进行路由的传参
<router-link class="nav" :to="{name:'a',params:{id:99}}">页面a</router-link>
<router-link class="nav" to="/b/67">页面b</router-link>
- 案例
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="../vue.js" harset="utf-8"></script> <script src="../vue-router.js" charset="utf-8"></script> <style type="text/css"> .nav { color: gray; margin-right: 10px; } .router-link-active { color: black } </style> </head> <body> <div id="div1"> <router-link class="nav" :to="{name:'a',params:{id:99}}">页面a</router-link> <router-link class="nav" to="/b/67">页面b</router-link> <br> 文字 <!-- 路由容器 --> <router-view></router-view> </div> </body> <script> // 路由表 let router = new VueRouter({ routes: [{ path: '/a', name:'a', component: { template: '<div>/a {{$route.params.id}}</div>' } }, { path: '/b/:id', name:'b', component: { template: '<div>/b {{$route.params.id}}</div>' } }] }); let vm = new Vue({ el: '#div1', data: {}, router }) </script> </html>
注:如果路由重复 如:path:'/a/:id'
和path:'/a/bbb'
;先匹配到谁就不会往下继续匹配
9.4 通过js控制路由跳转
this.$router.push()
控制跳转push(string | object)
入栈replace(string | object)
替换最后一个历史记录(当前)go(int)
- 可以使用 path 和 name 进行跳转
this.$router.push('/a/18');
this.$router.push({name:'b',params:{id:20}});
- 案例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="../vue.js" harset="utf-8"></script> <script src="../vue-router.js" charset="utf-8"></script> <style type="text/css"> .nav { color: gray; margin-right: 10px; } .router-link-active { color: black } </style> </head> <body> <div id="div1"> <input type="button" value="页面a" @click="fn1()"> <input type="button" value="页面b" @click="fn2()"> <br> 文字 <!-- 路由容器 --> <router-view></router-view> </div> </body> <script> // 路由表 let router = new VueRouter({ routes: [{ path: '/a/:id', name:'a', component: { template: '<div>/a {{$route.params.id}}</div>' } }, { path: '/b/:id', name:'b', component: { template: '<div>/b {{$route.params.id}}</div>' } }] }); let vm = new Vue({ el: '#div1', data: {}, router, methods:{ fn1(){ this.$router.push('/a/18'); }, fn2(){ this.$router.push({name:'b',params:{id:20}}); } } }) </script> </html>
9.5 $route 和 $router
- $route 当前路由信息
- $router 操作路由
9.6 监视路由
- 使用 watch —— 不推荐
- 简单
- 只能看不能干预
- 案例:
打印顺序<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="../vue.js" harset="utf-8"></script> <script src="../vue-router.js" charset="utf-8"></script> <style type="text/css"> .nav { color: gray; margin-right: 10px; } .router-link-active { color: black } </style> </head> <body> <div id="div1"> <input type="button" value="页面a" @click="fn1()"> <input type="button" value="页面b" @click="fn2()"> <br> 文字 <!-- 路由容器 --> <router-view></router-view> </div> </body> <script> // 路由表 let router = new VueRouter({ routes: [{ path: '/a/:id', name: 'a', component: { template: '<div>/a {{$route.params.id}}</div>' } }, { path: '/b/:id', name: 'b', component: { template: '<div>/b {{$route.params.id}}</div>' } }] }); let vm = new Vue({ el: '#div1', data: {}, router, methods: { fn1() { this.$router.push('/a/18'); }, fn2() { this.$router.push({ name: 'b', params: { id: 20 } }); } }, watch: { $route(value, old_value) { console.log(value, old_value); } } }) </script> </html>
console.log(value, old_value);
- 导航守卫
- 能监视 和控制
9.7 多视图
-
使用 name 属性给视图起名(命名视图),如:
<router-view name="header"></router-view>
-
可以有一个不给名字,默认为 default
-
{ path: '/', name: 'inddex', components: { default: indexCmp, header: headerCmp, footer: footerCmp } }
-
-
案例:
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title></title> <script src="../vue.js" harset="utf-8"></script> <script src="../vue-router.js" charset="utf-8"></script> </head> <body> <div id="div1"> <router-link to="/">首页</router-link> <router-link to="/news">新闻</router-link> <router-link to="/user">用户</router-link> <router-view name="header"></router-view> <router-view></router-view> <router-view name="footer"></router-view> </div> </body> <script> let indexCmp = { template: '<div>首页</div>' }; let newsCmp = { template: '<div>新闻</div>' }; let userCmo = { template: '<div>用户</div>' }; let headerCmp = { template: '<div>头部</div>' }; let footerCmp = { template: '<div>底部</div>' }; let router = new VueRouter({ routes: [{ path: '/', name: 'inddex', components: { default: indexCmp, header: headerCmp, footer: footerCmp } },{ path: '/news', name: 'inddex', components: { default: newsCmp, header: headerCmp, footer: footerCmp } },{ path: '/user', name: 'inddex', components: { default: userCmo, header: headerCmp, footer: footerCmp } }] }) let vm = new Vue({ el: '#div1', data: {}, router }) </script> </html>
9.8 嵌套路由
-
文件目录
-
初始化项目
cnpm init -y
-
安装环境
cnpm i vue vue-router -D
-
webpack.config.js
const path = require('path'); module.exports = { mode: 'development', entry: './src/vm.js', output: { path: path.resolve(__dirname, 'dest'), filename: 'bundle.min.js' } }
-
vm.js
import Vue from 'vue/dist/vue.esm'; import VueRouter from 'vue-router'; // 引入路由表 import router from './router'; // vue-router 需要挂在到 vue Vue.use(VueRouter); let vm = new Vue({ el:'#div1', data:{}, router:router });
-
router.js
import VueRouter from 'vue-router'; import Header from './components/headers'; import Home from './components/home'; import News from './components/news' import News1 from './components/news1' import News2 from './components/news2' export default new VueRouter({ routes: [{ path: '/index', name: '', components: { header: Header, default: Home } }, { path: '/news', name: 'news', components: { header: Header, default: News }, // 子路由 children: [ { path: '/news1', name: 'news1', components: { default: News1 } },{ path: '/news2', name: 'news2', components: { default: News2 } } ] }] });
-
header.js
export default { template: ` <div class="nav"> <router-link class="nav-item" to="/index">首页</router-link> <router-link class="nav-item" to="/news">新闻</router-link> </div> ` }
-
home.js
export default { template:` <div> 首页 </div> ` }
-
news.js 这里面做了路由嵌套
export default { template: ` <div> <router-link :to="{name:'news1'}">新闻一</router-link> <router-link :to="{name:'news2'}">新闻二</router-link><br> <h1>新闻</h1> <router-view></router-view> </div> ` }
-
news1.js
export default { template:` <div> 新闻1 </div> ` }
-
news2.js
export default { template:` <div> 新闻2 </div> ` }
-
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="div1"> <router-view name="header"></router-view> <router-view></router-view> </div> </body> <script src="dest/bundle.min.js"></script> </html>
-
结果
10. 数据交互
-
get请求
- vm.js
import Vue from 'vue/dist/vue.esm'; let vm = new Vue({ el: '#div1', data: { src: '', loaded: false }, async created() { let res =await fetch('http://localhost:8080/api/demo'); let data =await res.json(); console.log(data); }, template: ` <div> <img :src="src" alt="" width="200"/> </div> ` })
- vm.js
-
post 请求 手动使用 FormData 添加数据
- vm.js
import Vue from 'vue/dist/vue.esm'; let vm = new Vue({ el: '#div1', data: { result: 0, }, async created() { let formdata = new FormData(); formdata.append('a', 10); formdata.append('b', 20); let res = await fetch('http://localhost:8080/api/post_demo', { method: 'post', body: formdata }); let data = await res.json(); this.result = data; }, template: ` <div> {{result}} </div> ` })
- vm.js
-
post 使用表单
- vm.js
import Vue from 'vue/dist/vue.esm'; let vm = new Vue({ el: '#div1', data: { a: 0, b: 0, result: 0 }, async created() { }, methods: { async fn_submit() { console.log(this.$refs); let form = this.$refs['form1']; let formdata = new FormData(this.$refs['form1']); let res = await fetch(form.action, { method: form.method, body: formdata }); let data = await res.json(); this.result = data; } }, template: ` <div> <form ref="form1" @click.prevent="fn_submit()" action="http://localhost:8080/api/post_demo" method="post"> <input type="text" name="a" v-model="a"> <input type="text" name="b" v-model="b"> {{result}} <input type="submit" value="计算"> </form> </div> ` })
- vm.js
11. 组件
组件也是 vm 对象
11.1 局部组件
-
注: 组件里面 data 是一个函数
-
文件目录
-
cmp1.js
import Vue from 'vue/dist/vue.esm'; let vm = new Vue({ el: '#div1', data: {}, // 局部组件 components: { cmp1: { data(){ return { a:10 } }, template: ` <div>{{a}}</div> ` } }, template:` <div><cmp1></cmp1></div> ` })
-
index.html 使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="div1"></div> </body> <script src="dest/bundle.js"></script> </html>
11.2 全局组件
-
全局组件使用
Vue.component()
-
文件目录
-
cmp1.js
import Vue from 'vue/dist/vue.esm'; // 全局组件 Vue.component('cmp1', { data() { return { a: 10, b: 20 } }, template: ` <div>{{a+b}}</div> ` });
-
vm.js
import Vue from 'vue/dist/vue.esm'; import './cmp1'; let vm = new Vue({ el:'#div1', data:{}, template:` <div><cmp1></cmp1></div> ` })
-
webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { mode: 'development', entry: './src/vm.js', output: { path: path.resolve(__dirname, 'dest'), filename: 'bundle.js' }, // 打包html plugins: [ new HtmlWebpackPlugin({ template: 'index.html' }) ], // 配置 webpack-dev-server devServer: { // 新版本 webpack 不支持 contentBase 应添加下面这段话,已显示目录 static: { directory: __dirname, }, open: true // 是否自动打开浏览器 } }
11.3 组件传参
-
需要使用
props
注册参数,传递过来的数据都是字符串,可以使用:
原样传递,如:
-
vm.js
import Vue from 'vue/dist/vue.esm'; import './cmp1'; let vm = new Vue({ el:'#div1', data:{}, template:` <div><cmp1 name="张三" :age="18"></cmp1></div> ` })
-
cmp1.js
import Vue from 'vue/dist/vue.esm'; // 全局组件 Vue.component('cmp1', { props:['name','age'], data() { return { a: 10, b: 20 } }, template: ` <div> 姓名:{{name}} 年龄:{{age+2}} </div> ` });
11.4 使用 component占位符 动态显示需要的组件
- vm.js
import Vue from 'vue/dist/vue.esm'; import './cmp1'; let vm = new Vue({ el:'#div1', data:{ type:'' }, template:` <div> <input type="text" v-model="type" /> <component :is="type" name="张三" :age="18"></component> </div> ` })
11.5 组件测试
将组件实例化出来但不显示在页面,可用于组件测试
- vm.js
import Vue from 'vue/dist/vue.esm'; import Cmp1 from './cmp1'; let cmp = new Cmp1({ propsData:{ name:"张三", age:18 } }) let vm = cmp.$mount(); console.log(vm.$el);
- cmp1.js
import Vue from 'vue/dist/vue.esm'; // 全局组件 let Cmp1 = Vue.component('cmp1', { props:['name','age'], data() { return { a: 10, b: 20 } }, template: ` <div> 姓名:{{name}} 年龄:{{age+2}} </div> ` }); export default Cmp1;
12 slot 插槽
插槽用于决定将所携带的内容,插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性。插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制。
- vm.js
import Vue from 'vue/dist/vue.esm'; import MySlot from './my-slot' let vm=new Vue({ el:'#div1', data:{}, template:` <div> <my-slot> 文字 </my-slot> </div> ` })
- my-slot.js
import Vue from 'vue/dist/vue.esm'; // 全局组件 Vue.component('my-slot', { data() { return {} }, template: ` <div> <slot /> </div> ` });
13 命名插槽
- vm.js
import Vue from 'vue/dist/vue.esm'; import MySlot from './my-slot' let vm=new Vue({ el:'#div1', data:{}, template:` <div> <my-slot> <template slot="title"> <h1>标题</h1> </template> 文字 <ul> <li></li> </ul> </my-slot> </div> ` })
- my-slot.js
import Vue from 'vue/dist/vue.esm'; // 全局组件 Vue.component('my-slot', { data() { return {} }, template: ` <div> <slot name="title" /> <slot /> </div> ` });
14. 生命周期
- created 数据操作
- mounted 操作元素
15 组件之间的通信
15.1 父子组件之间的通信 —— 直接拿引用 (1)
优点:简单
缺点:耦合度非常高
- 文件目录
① 父级找子级
父级中,直接在子集标签上加 ref
,父级可直接通过 this.$refs.ref属性值.数据/方法
访问子集中的数据和方法
- child.js
import Vue from 'vue'; export default Vue.component('child', { data() { return { num: 0 } }, methods: { al_a() { this.num++; console.log('子集方法'); } }, template: ` <div> <h3>子集</h3> {{num}} </div> ` })
- parent.js
import Vue from 'vue'; import Child from './child'; export default Vue.component('parent', { methods: { fn() { // 使用 ref 获取子组件的数据及方法 this.$refs.c1.al_a(); console.log(this.$refs.c1.num); } }, template: ` <div> <h1>父级 <input type="button" value="+1" @click="fn()"/></h1> <child ref="c1"></child> </div> ` })
- index.js
import Vue from 'vue'; import Parent from './components/parent'; let vm = new Vue({ el:'#div1', data:{}, components:{ Parent }, template:` <div> <parent></parent> </div> ` })
② 子级找父级
- 反向的将父类暴露给子类
- child.js
import Vue from 'vue'; // 子 -> 父 export default Vue.component('child', { props: ['parent'], data() { return { num: 0 } }, methods: { fn() { // 调用父类方法 this.parent.add(); console.log(this.parent.num); } }, template: ` <div> <h3>子集 <input type="button" value="+1" @click="fn()"/></h3> </div> ` })
- parent.js
import Vue from 'vue'; import Child from './child'; //子 -> 父 export default Vue.component('parent', { data() { return {} }, methods:{ add(){ console.log("父类方法"); this.num++ } }, template: ` <div> <h1>父级 {{num}}</h1> <child :parent="this"></child> </div> ` })
- index.js
import Vue from 'vue'; import Parent from './components/parent'; let vm = new Vue({ el:'#div1', data:{}, components:{ Parent }, template:` <div> <parent></parent> </div> ` })
15.2 父子组件之间的通信 —— 组件事件
降低耦合度
① 父传子
- parent.js
import Vue from 'vue'; import Child from './child'; export default Vue.component('parent', { data() { return {} }, methods: { fn(){ this.$refs.c1.$emit('add_num',7); } }, template: ` <div> <h1>父级 <input type="button" value="+1" @click="fn()"/></h1> <child ref="c1"/> </div> ` })
- child.js
import Vue from 'vue'; export default Vue.component('child', { data() { return { num: 0 } }, methods: {}, template: ` <div> <h3>子集 {{num}}</h3> </div> `, created() { this.$on('add_num', function (n) { console.log(n); this.num += n; }) } })
② 子传父
- parent.js
import Vue from 'vue'; import Child from './child'; export default Vue.component('parent', { data() { return { num: 0 } }, methods: { fn() { this.$refs.c1.$emit('add_num', 7); } }, template: ` <div> <h1>父级 {{num}}</h1> <child :parent="this"/> </div> `, created() { this.$on('add_num', function (n) { this.num += n; }) } })
- child.js
import Vue from 'vue'; export default Vue.component('child', { props: ['parent'], data() { return { num: 0 } }, methods: { fn() { this.parent.$emit("add_num", 2); } }, template: ` <div> <h3>子集 <input type="button" value="+1" @click="fn()"/></h3> </div> ` })
16. webpack 打包 vue2.0
-
文件目录
-
安装环境:
cnpm i vue-loader vue-style-loader vue-html-loader vue-template-compiler -D
-
vue-loader
解析和转换 .vue 文件,提取出其中的逻辑代码 script、样式代码 style、以及 HTML 模版 template,再分别把它们交给对应的 Loader 去处理。 -
vue-style-loader
解析 vue 内部 style;将外部 css 和 vue的css 处理到一起 -
vue-html-loader
解析 vue 内部 template -
vue-template-compiler
编译器,将vue组件编译出来 -
webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); module.exports = { mode: 'development', entry: './src/index.js', output: { path: path.resolve(__dirname, 'dest'), filename: 'bundle.js' }, module:{ rules:[ {test:/\.css$/i,use:['vue-style-loader','css-loader']}, {test:/\.vue$/i,use:'vue-loader'} ] }, resolve:{ alias:{ 'vue':'vue/dist/vue.esm' } }, // 打包html plugins: [ new HtmlWebpackPlugin({ template: 'index.html' }), new VueLoaderPlugin() ], // 配置 webpack-dev-server devServer: { // 新版本 webpack 不支持 contentBase 应添加下面这段话,已显示目录 static: { directory: __dirname, }, open: true // 是否自动打开浏览器 } }
-
cmp1.vue
<template> <div class="box"> cmp1 </div> </template> <script> export default { name: 'cmp1', // 可选;调试时候,报错的时候会报这个 name,没有就报文件名,利于vue调试工具的使用 data() { return { } }, } </script> // 添加 scoped 表示样式只在当前起作用 <style lang="css" scoped> .box{ width:200px; height: 200px; background: yellow; } </style>
-
index.js
import Vue from 'vue'; import Cmp1 from './components/cmp1.vue' let vm = new Vue({ el:'#div1', data:{}, components:{ Cmp1 }, template:` <div> <Cmp1 /> </div> ` })
17. vue-router(vue2.0)
了解 2.0 组件写法及 vue-router 基本使用
- 文件结构
- index.js
import Vue from 'vue'; import App from './App.vue'; import router from './routers' let vm = new Vue({ el: '#div1', data: {}, template: '<App />', router,//路由表 components: { App } })
- routers / index.js
import Vue from 'vue'; import Router from 'vue-router'; import Index from '@/index.vue'; import News from '@/news.vue'; Vue.use(Router); export default new Router({ routes: [{ path: '/', name: 'index', component: Index }, { path: '/news', name: 'news', component: News } ] })
- app.vue
<template> <div> <router-link to="/">首页</router-link> <router-link to="/news">新闻</router-link> <router-view></router-view> </div> </template> <script> export default { name:'app', data() { return { } }, } </script> <style lang="css"> </style>
- webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); module.exports = { mode: 'development', entry: './src/index.js', output: { path: path.resolve(__dirname, 'dest'), filename: 'bundle.js' }, module:{ rules:[ {test:/\.css$/i,use:['vue-style-loader','css-loader']}, {test:/\.vue$/i,use:'vue-loader'}, {test:/\.less$/i,use:['vue-style-loader','css-loader','less-loader']} ] }, resolve:{ alias:{ 'vue':'vue/dist/vue.esm', '@':path.resolve(__dirname,'src/components/') } }, // 打包html plugins: [ new HtmlWebpackPlugin({ template: 'index.html' }), new VueLoaderPlugin() ], // 配置 webpack-dev-server devServer: { // 新版本 webpack 不支持 contentBase 应添加下面这段话,已显示目录 static: { directory: __dirname, }, open: true // 是否自动打开浏览器 } }
- index.vue
<template> <div> index </div> </template> <script> export default { name:"index", data() { return { } }, } </script> <style lang="css"> </style>
- news.vue
<template> <div> news </div> </template> <script> export default { name:'news', data() { return { } }, } </script> <style lang="css"> </style>
- 结果
18. vue-cli(启动器 / 脚手架)
- 安装:
cnpm i vue-cli -g
- 初始化项目
vue init webpack project1
- 项目结构
19. vuex
官网:Vuex