原先结构:
<!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>Document</title> </head> <body> <div id="app"> <header id="header">这里是头部</header> <div id="content">这里是内容区域</div> <footer id="footer">这里是底部区域</footer> </div> </body> <script src="vue.js"></script> <script> new Vue({ el: '#app' }) </script> </html>
抽离头部组件(可读性差):
<!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>Document</title> </head> <body> <div id="app"> <!-- <header id="header">这里是头部</header> --> <my-header></my-header> <div id="content">这里是内容区域</div> <!-- <footer id="footer">这里是底部区域</footer> --> <my-footer /> </div> </body> <script src="vue.js"></script> <script> // 定义一个组件 var Header = { // 组件的首字母大写,template 表示该组件的 模板,类型是字符串 template: `<header id="header">这里是头部----组件</header>` } var Footer = { // 组件的首字母大写,template 表示该组件的 模板,类型是字符串 template: `<footer id="footer">这里是底部区域 ----组件</footer>` } // 注册组件 ---- 全局注册组件 ---- 一定是在new Vue之前 // Vue.component('自定义的组件名', 定义的组件) --- 以标签的形式调用 组件名 即可 Vue.component('my-header', Header) // <my-header></my-header> <my-header /> Vue.component('my-footer', Footer) new Vue({ el: '#app' }) </script> </html>
抽离组件的模板:
<!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>Document</title> </head> <body> <div id="app"> <my-header></my-header> </div> </body> <!-- script 表示的模板的方式 只需要了解即可 --> <!-- <script type="text/x-template" id="header"> <header class="header"> <button>返回</button> 搜索 </header> </script> --> <!-- 使用template标签定义模板 是推荐使用的 --> <template id="header"> <header class="header"> <button>返回</button> 搜索 </header> </template> <script src="vue.js"></script> <script> const Header = { template: '#header' } Vue.component('my-header', Header) new Vue({ el: '#app' }) </script> </html>
组建的选项:
<!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>Document</title> </head> <body> <div id="app"> <my-header></my-header> </div> </body> <template id="header"> <header class="header"> <button @click="back">返回</button> <input type="text" v-model="msg"> {{ msg }} - {{ msgTip }} </header> </template> <script src="vue.js"></script> <script> // 组件是 特殊 的Vue实例 const Header = { template: '#header', data () { // 组件中的初始化数据, data必须是一个函数,返回一个对象 ---- 特殊点 return { msg: '搜索中.......' } }, methods: { back () { console.log('你点击了返回按钮') } }, computed: { msgTip () { if (this.msg === '') { return '请输入搜索内容' } else { return this.msg } } } } Vue.component('my-header', Header) new Vue({ el: '#app', }) </script> </html>
局部注册组件:
<!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>Document</title> </head> <body> <div id="app"> <my-header></my-header> </div> </body> <template id="header"> <header class="header"> <my-back></my-back> <input type="text" v-model="msg"> {{ msg }} - {{ msgTip }} </header> </template> <template id="back"> <button @click="back">返回</button> </template> <script src="vue.js"></script> <script> const Back = { template: '#back', methods: { back () { console.log('你点击了返回按钮') } } } // 组件是 特殊 的Vue实例 const Header = { template: '#header', data () { // 组件中的初始化数据, data必须是一个函数,返回一个对象 ---- 特殊点 return { msg: '搜索中.......' } }, components: { 'my-back': Back //在谁的局部注册,就只能在谁的内部使用 }, computed: { msgTip () { if (this.msg === '') { return '请输入搜索内容' } else { return this.msg } } } } // Vue.component('my-back', Back) new Vue({ el: '#app', data: { msg: '搜索...' }, components: { 'my-header': Header } }) </script> </html>
父子组件
<!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>Document</title> </head> <body> <div id="app"> <my-list></my-list> </div> </body> <template id="list"> <ul> <li> <img src="" alt="" style="display:block; 150px;height: 150px;border: 1px solid #f66"> 名称 </li> </ul> </template> <script src="vue.js"></script> <script> const List = { template: '#list' } // 全局注册组件 // Vue.component('my-list', List) new Vue({ el: '#app', components: { 'my-list': List } }) </script> </html>
父组件给子组件传值1:
父组件在调用子组件的地方,添加一个自定义的属性,属性的值就是需要传递给子组件的值,如果需要传递的值是一个变量,或者是boolean,或者是number类型,需要使用绑定属性,在子组件定义的地方,添加一个选项 props,方式一 props的值为数组,元素为自定义的属性名
子组件的注册必须在父组件 ,或者直接使用全局注册组件
app 为 父,header 为子
header 为 父,back 为子
<!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>Document</title> </head> <body> <div id="app"> <my-list :list="list" :num="list.length"></my-list> </div> </body> <template id="list"> <ul> <li v-for="(item, index) of list" :key = "index"> <img :src="item.img" alt="" style="display:block; 150px;height: 150px;border: 1px solid #f66"> {{ item.title }} </li> </ul> </template> <script src="vue.js"></script> <script> const List = { props: ['list'], template: '#list' } // 全局注册组件 // Vue.component('my-list', List) new Vue({ el: '#app', data: { list: [{ img: 'https://m.360buyimg.com/mobilecms/s750x750_jfs/t23491/230/1714105950/260598/44036572/5b680745N534fe714.jpg!q80.dpg.webp', title: '七匹狼中长款夹克男春季新款时尚立领外套休闲男装茄克衫 001(黑色) 175/92A(XL)' }, { img:'https://m.360buyimg.com/mobilecms/s750x750_jfs/t23386/9/1066712099/277967/615ccafb/5b4f0e3aN262237fc.jpg!q80.dpg.webp', title: '【官方AppleCare+版】Apple MacBook Pro 15.4英寸笔记本电脑 深空灰色 配备Touch Bar 2018新款(八代i5/16G)' }] }, components: { 'my-list': List } }) </script> </html>
父组件给子组件传值2:
在子组件定义的地方,添加一个选项 props, 方式二 props的值是一个对象,key值为自定义的属性名,value值为 数据类型 ---- 团队合作 提升代码的严谨性,如果类型不对,会有警告信息出现,但是不会阻止你的代码的渲染
<!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>Document</title> </head> <body> <div id="app"> <my-list :list="list" :num="list.length"></my-list> </div> </body> <template id="list"> <ul> <li v-for="(item, index) of list" :key = "index"> <img :src="item.img" alt="" style="display:block; 150px;height: 150px;border: 1px solid #f66"> {{ item.title }} </li> </ul> </template> <script src="vue.js"></script> <script> const List = { props: { list: Number }, template: '#list' } // 全局注册组件 // Vue.component('my-list', List) new Vue({ el: '#app', data: { list: [{ img: 'https://m.360buyimg.com/mobilecms/s750x750_jfs/t23491/230/1714105950/260598/44036572/5b680745N534fe714.jpg!q80.dpg.webp', title: '七匹狼中长款夹克男春季新款时尚立领外套休闲男装茄克衫 001(黑色) 175/92A(XL)' }, { img:'https://m.360buyimg.com/mobilecms/s750x750_jfs/t23386/9/1066712099/277967/615ccafb/5b4f0e3aN262237fc.jpg!q80.dpg.webp', title: '【官方AppleCare+版】Apple MacBook Pro 15.4英寸笔记本电脑 深空灰色 配备Touch Bar 2018新款(八代i5/16G)' }] }, components: { 'my-list': List } }) </script> </html>
父组件给子组件传值3:
在子组件定义的地方,添加一个选项 props, 方式三 props的值是一个对象, key值是自定义的属性名,value值为一个对象,这个对象的key值分别为 type 和 default,表示数据类型和默认值,如果数据类型是 对象和 数组,默认值必须写为函数,其余直接赋值
<!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>Document</title> </head> <body> <div id="app"> <my-list :list="list" :num="list.length"></my-list> <hr /> <my-list></my-list> </div> </body> <template id="list"> <ul> <li v-for="(item, index) of list" :key = "index"> <img :src="item.img" alt="" style="display:block; 150px;height: 150px;border: 1px solid #f66"> {{ item.title }} </li> </ul> </template> <script src="vue.js"></script> <script> const List = { props: { list: { type: Array, default: function () { return [ { img: '1', title: '您还没有传值哦' } ] } } }, template: '#list' } // 全局注册组件 // Vue.component('my-list', List) new Vue({ el: '#app', data: { list: [{ img: 'https://m.360buyimg.com/mobilecms/s750x750_jfs/t23491/230/1714105950/260598/44036572/5b680745N534fe714.jpg!q80.dpg.webp', title: '七匹狼中长款夹克男春季新款时尚立领外套休闲男装茄克衫 001(黑色) 175/92A(XL)' }, { img:'https://m.360buyimg.com/mobilecms/s750x750_jfs/t23386/9/1066712099/277967/615ccafb/5b4f0e3aN262237fc.jpg!q80.dpg.webp', title: '【官方AppleCare+版】Apple MacBook Pro 15.4英寸笔记本电脑 深空灰色 配备Touch Bar 2018新款(八代i5/16G)' }] }, components: { 'my-list': List } }) </script> </html>
子组件给父组件传值:
在父组件调用子组件的地方,给它绑定一个自定义的事件,事件的执行是由父组件执行,记住不要加()
在子组件定义的地方,在需要传值的函数内部,执行 this.$emit('自定义的事件名', '传递的值') ---- 触发自定义的事件
<!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>Document</title> </head> <body> <div id="app"> <h1>这里是父组件</h1> <h2>子组件的值是: {{ msg }}</h2> <my-child @to-parent="getdata"></my-child> </div> </body> <template id="child"> <div> <button @click="sendData">给父组件传值</button> </div> </template> <script src="vue.js"></script> <script> const Child = { template: '#child', methods: { sendData () { this.$emit('to-parent', '啦啦啦啦啊啊啦') } } } new Vue({ el: '#app', data: { msg: '' }, components: { 'my-child': Child }, methods: { getdata (val) { this.msg = val } } }) </script> </html>
非父子组件:
<!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>Document</title> </head> <body> <div id="app"> <my-content></my-content> <my-footer></my-footer> </div> </body> <template id="content"> <div> 内容 </div> </template> <template id="footer"> <ul> <li>首页</li> <li>分类</li> <li>购物车</li> <li>我的</li> </ul> </template> <script src="vue.js"></script> <script> const Content = { template: '#content' } const Footer = { template: '#footer' } new Vue({ el: '#app', components: { 'my-content': Content, 'my-footer': Footer } }) </script> </html>
非父子组件传值:
利用 中央事件总线 进行传值,利用new Vue()实例作为中央事件总线
const bus = new 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>Document</title> </head> <body> <div id="app"> <my-content></my-content> <my-footer></my-footer> </div> </body> <template id="content"> <div> 内容 - {{ type }} </div> </template> <template id="footer"> <ul> <li @click="changeContent('首页')">首页</li> <li @click="changeContent('分类')">分类</li> <li @click="changeContent('购物车')">购物车</li> <li @click="changeContent('我的')">我的</li> </ul> </template> <script src="vue.js"></script> <script> const bus = new Vue() const Content = { template: '#content', data () { return { type: '' } }, mounted () { // 用来监听点击的是哪一个 bus.$on('footer-content', (val) => { this.type = val }) } } const Footer = { template: '#footer', methods: { changeContent (val) { bus.$emit('footer-content', val) } }, mounted () { bus.$emit('footer-content', '首页') } } new Vue({ el: '#app', components: { 'my-content': Content, 'my-footer': Footer } }) </script> </html>
注册表单
使用 component 标签结合is属性,指明使用的是哪一个组件
<!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>Document</title> </head> <body> <div id="app"> <button @click="type='my-phone'">手机号注册</button> <button @click="type='my-email'">邮箱注册</button> <button @click="type='my-user'">用户名注册</button> <component :is="type"></component> <!-- <component is="my-user"></component> --> <!-- <my-phone></my-phone> <my-email></my-email> <my-user></my-user> --> </div> </body> <template id="phone"> <div> <input type="text" placeholder="手机号"> </div> </template> <template id="email"> <div> <input type="text" placeholder="邮箱"> </div> </template> <template id="user"> <div> <input type="text" placeholder="用户名"> </div> </template> <script src="vue.js"></script> <script> const Phone = { template: '#phone' } const Email = { template: '#email' } const User = { template: '#user' } new Vue({ el: '#app', data: { type: 'my-phone' }, components: { 'my-phone': Phone, 'my-email': Email, 'my-user': User } }) </script> </html>
保留组件状态
使用 keep-alive 包裹 component 标签即可完成 保留组件的状态
<!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>Document</title> </head> <body> <div id="app"> <button @click="type='my-phone'">手机号注册</button> <button @click="type='my-email'">邮箱注册</button> <button @click="type='my-user'">用户名注册</button> <keep-alive> <component :is="type"></component> </keep-alive> </div> </body> <template id="phone"> <div> <input type="text" placeholder="手机号"> </div> </template> <template id="email"> <div> <input type="text" placeholder="邮箱"> </div> </template> <template id="user"> <div> <input type="text" placeholder="用户名"> </div> </template> <script src="vue.js"></script> <script> const Phone = { template: '#phone', mounted () { console.log('phone') } } const Email = { template: '#email', mounted () { console.log('email') } } const User = { template: '#user', mounted () { console.log('user') } } new Vue({ el: '#app', data: { type: 'my-phone' }, components: { 'my-phone': Phone, 'my-email': Email, 'my-user': User } }) </script> </html>
新增钩子函数
keep-alive 保留组件的状态,避免组件的重新渲染 --- 不会再次触发mounted钩子函数,如果想要了解到执行到哪一个选项,可以使用 activated 和 deactivated 钩子函数
使用了 keep-alive 才有钩子
<!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>Document</title> </head> <body> <div id="app"> <button @click="type='my-phone'">手机号注册</button> <button @click="type='my-email'">邮箱注册</button> <button @click="type='my-user'">用户名注册</button> <keep-alive> <component :is="type"></component> </keep-alive> </div> </body> <template id="phone"> <div> <input type="text" placeholder="手机号"> </div> </template> <template id="email"> <div> <input type="text" placeholder="邮箱"> </div> </template> <template id="user"> <div> <input type="text" placeholder="用户名"> </div> </template> <script src="vue.js"></script> <script> const Phone = { template: '#phone', mounted () { console.log('phone') }, activated () { console.log('phone 被激活') }, deactivated () { console.log('phone 被隐藏') } } const Email = { template: '#email', mounted () { console.log('email') }, activated () { console.log('email 被激活') }, deactivated () { console.log('email 被隐藏') } } const User = { template: '#user', mounted () { console.log('user') }, activated () { console.log('user 被激活') }, deactivated () { console.log('user 被隐藏') } } new Vue({ el: '#app', data: { type: 'my-phone' }, components: { 'my-phone': Phone, 'my-email': Email, 'my-user': User } }) </script> </html>
部分保留组件状态:
* 定义组件添加 name属性,keep-alive 添加include 属性,记住不要加空格
<!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>Document</title> </head> <body> <div id="app"> <button @click="type='my-phone'">手机号注册</button> <button @click="type='my-email'">邮箱注册</button> <button @click="type='my-user'">用户名注册</button> <keep-alive include="phone,email"> <component :is="type"></component> </keep-alive> </div> </body> <template id="phone"> <div> <input type="text" placeholder="手机号"> </div> </template> <template id="email"> <div> <input type="text" placeholder="邮箱"> </div> </template> <template id="user"> <div> <input type="text" placeholder="用户名"> </div> </template> <script src="vue.js"></script> <script> const Phone = { name: 'phone', template: '#phone' } const Email = { name: 'email', template: '#email' } const User = { name: 'user', template: '#user' } new Vue({ el: '#app', data: { type: 'my-phone' }, components: { 'my-phone': Phone, 'my-email': Email, 'my-user': User } }) </script> </html>
VUE脚手架安装操作:
cnpm i @vue/cli@3 -g
> vue create myapp
* 选择 Manually select features ----- 自选预设文件
* 选择 vue 项目的 东西 除了typesctipt 不选,其余都选 --- 选择用空格
* 选择应用的历史模式 Y
* 选择样式文件 sass/scss (with node-sass)
* 代码的校验规则 ESLint + Standard config
* Lint on save 保存检验代码格式
* 测试的模式 Mocha + Chai
* 运行模式 Cypress (Chrome only)
* 配置文件在哪里 In package.json
* 是否保存预设文件 y (以后直接一开始就选择预设文件,后续不需要自动选择)