一、创建组件

什么是组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可;
组件化和模块化的不同:

  • 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一;
  • 组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用;

1.1 创建全局组件

  • 注意:模板中有且只有一个根元素,就像一个网页只有一个根元素为<html>一样

1.1.1 第一种方式

  • 使用 Vue.extend 配合 Vue.component 方法:
  1. var com1 = Vue.extend({
  2. template: '<h2>第一种方式</h2>',
  3. data(){
  4. return{
  5. }
  6. }
  7. });
  8. Vue.component('mycom1', com1);
  • 直接使用 Vue.component 方法:
  1. Vue.component('mycom2', {
  2. template: '<h4>第一种方式的最终简写</h4>',
  3. data(){
  4. return {
  5. }
  6. }
  7. });

1.1.2 第二种创建方式

  • 将模板字符串,定义到script标签中:
  1. <script id="tmpl" type="template">
  2. <div>
  3. <a href="#">{{msg.login}}</a> | <a href="#">{{msg.register}}</a>
  4. </div>
  5. </script>
  • 同时,需要使用 Vue.component 来定义组件:
  1. Vue.component('account', {
  2. template: '#tmpl',
  3. data(){
  4. return{
  5. msg: {login: '登录',register: '注册'}
  6. }
  7. }
  8. });

1.1.2 第三种创建方式

  • <template>中定义 HTML 结构
  1. <template id="tmp">
  2. <div>
  3. <h2>第二种方式</h2>
  4. <h3 v-text="msg"></h3>
  5. </div>
  6. </template>

使用 Vue.component 中 template 属性 进行调用

  1. Vue.component('myCom3', {
  2. template: '#tmp',
  3. data(){
  4. return{
  5. msg: '好耶~'
  6. }
  7. }
  8. });

1.2 创建私有组件

  1. new Vue({
  2. el: '#app',
  3. data: {},
  4. methods:{},
  5. filters:{},
  6. directives:{},
  7. components: {
  8. myLogin: {
  9. template: `<div>
  10. <h1>login 组件</h1>
  11. <p>私有组件</p>
  12. </div>`,
  13. data(){
  14. return{
  15. }
  16. }
  17. }
  18. },
  19. beforeCreate(){},
  20. created(){},
  21. beforeMount(){},
  22. Mounted(){},
  23. beforeUpdate(){},
  24. updated(){},
  25. beforeDestroy(){},
  26. destroyed(){}
  27. });

注意: 组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹!

分离私有组件的定义,将组件模板对象分开定义

  1. const myLogin = {
  2. template: `<div>
  3. <h1>login 组件</h1>
  4. <p>私有组件</p>
  5. </div>`,
  6. data(){
  7. return{
  8. }
  9. }
  10. }
  11. new Vue({
  12. el: '#app',
  13. data: {},
  14. methods:{},
  15. components: {
  16. // 组件名称:组件的模板对象
  17. // myLogin: myLogin
  18. myLogin,
  19. }
  20. });

1.3 组件的嵌套

我们的子组件是可以多级嵌套的,子组件中还可以在声明子组件。

  1. <div id="app">
  2. <account></account>
  3. </div>
  4. <script>
  5. // 创建 Vue 实例,得到 ViewModel
  6. var vm = new Vue({
  7. el: '#app',
  8. data: {},
  9. methods: {},
  10. components: {
  11. account: {
  12. template: `<div>
  13. <h1>这是Account组件{{name}}</h1>
  14. <login></login>
  15. <register></register>
  16. </div>`,
  17. data(){
  18. return {
  19. name: 'hello world!'
  20. }
  21. },
  22. components: {
  23. login: {
  24. template: "<h3>这是登录组件</h3>"
  25. },
  26. register: {
  27. template: '<h3>这是注册组件</h3>'
  28. }
  29. }
  30. }
  31. }
  32. });
  33. </script>

1.4 组件使用方式

  • 在HTML中直接用标签应用即可
  1. <div id="app">
  2. <mycom1></mycom1>
  3. <mycom2></mycom2>
  4. <my-com3></my-com3>
  5. <my-login></my-login>
  6. </div>
  • 非驼峰命名法,则直接照着写就好了
  • 驼峰命名法:定义时使用驼峰,使用时需要以 “ - ” 分隔

二、组件中展示数据和响应事件

  • data 定义方式不太一样,但是使用方式和实例中的 data 没什么太大区别
  • 因为组件需要被重用,为了让每个组件有自己的私有作用域,data需要为函数并且返回一个对象

  • 在组件中,data需要被定义为一个方法

通过计数器案例演示

  1. <div id="app">
  2. <myCount></myCount><hr>
  3. <myCount></myCount><hr>
  4. <myCount></myCount><hr>
  5. </div>
  6. <template id="tmp">
  7. <input type="button" value="+1" @click="increment">
  8. <h3 v-text="count"></h3>
  9. </template>
  10. <script>
  11. Vue.component('myCount',{
  12. template: '#tmp',
  13. data: function (){
  14. return {
  15. count: 0
  16. }
  17. },
  18. methods:{
  19. increment(){
  20. this.count++;
  21. }
  22. }
  23. });
  24. new Vue({
  25. el:'#app',
  26. data:{ },
  27. methods:{ }
  28. })
  29. </script>

三、组件切换

3.1 使用 v-if 和 v-else 配合进行切换

  1. <div id="app">
  2. <a href="" @click.prevent="flag = true">登录</a>
  3. <a href="" @click.prevent="flag = false">注册</a>
  4. <transition mode="out-in">
  5. <login v-if="flag"></login>
  6. <register v-else="flag"></register>
  7. </transition>
  8. </div>
  9. <script>
  10. Vue.component('login',{
  11. template: '<h3>登录组件</h3>'
  12. })
  13. Vue.component('register',{
  14. template: '<h3>注册组件</h3>'
  15. })
  16. new Vue({
  17. el: '#app',
  18. data:{
  19. flag: true,
  20. },
  21. methods:{
  22. }
  23. })
  24. </script>

进行切换" class="reference-link">3.2 使用<component>进行切换

  • :is 指定要显示的组件名称
  1. <style>
  2. .v-enter,
  3. .v-leave-to {
  4. opacity: 0;
  5. transform: translateX(150px);
  6. }
  7. .v-enter-active,
  8. .v-leave-active {
  9. transition: all .5s ease;
  10. }
  11. </style>
  12. <div id="app">
  13. <a href="" @click.prevent="comName = 'login'">登录</a>
  14. <a href="" @click.prevent="comName = 'register'">注册</a>
  15. <a href="" @click.prevent="comName = 'forget'">忘记密码</a>
  16. <!-- 通过mode属性,设置组件切换时候的模式 -->
  17. <transition mode="out-in">
  18. <component :is="comName"></component>
  19. </transition>
  20. </div>
  21. <script>
  22. Vue.component('login',{
  23. template: '<h3>登录组件</h3>'
  24. })
  25. Vue.component('register',{
  26. template: '<h3>注册组件</h3>'
  27. })
  28. Vue.component('forget',{
  29. template: '<h3>忘记密码组件</h3>'
  30. })
  31. new Vue({
  32. el: '#app',
  33. data:{
  34. comName: 'login'
  35. },
  36. methods:{
  37. }
  38. })
  39. </script>

四、父组件向子组件传值(属性)

  • 注意:一定要使用props属性来定义父组件传递过来的数据,由于 props 默认是单向绑定,即父组件的属性发生改变,子组件也会改变。反之则不会。props中数据是只读的

  • 使用v-bind或简化指令:,将数据传递到子组件中

  1. <div id="app">
  2. <!-- 父组件可以在引用子组件的时候,通过属性绑定的形式 ,把需要传递给子组件的数据,以属性绑定的形式传递到子组件内部供使用-->
  3. <my-component v-bind:parentmsg="msg" :msg="message"></my-component>
  4. </div>
  5. <script>
  6. Vue.component('myComponent', {
  7. template: '<span>{{ parentmsg }}--{{msg}}</span>',
  8. // props:声明待接收的父组件数据
  9. // props中数据都是只读的,无法重新赋值
  10. props: ['parentmsg', 'msg'],
  11. // 把父组件传递过来的parentmsg和msg属性,现在props数组中定义一下,这样才能使用父组件传递过来的数据
  12. data() {
  13. return {
  14. // 子组件中也可以有自己的数据在data中
  15. }
  16. }
  17. })
  18. var vm = new Vue({
  19. el: '#app',
  20. data: {
  21. msg: 'hello 子组件',
  22. message: 'hello world'
  23. }
  24. })
  25. </script>

绑定修饰符

  1. <div id="app">
  2. <!-- 默认 单向绑定 -->
  3. <my-component :msg="message"></my-component>
  4. <!-- 双向绑定 -->
  5. <my-component :msg.sync="message"></my-component>
  6. <!-- 单向绑定 -->
  7. <my-component :msg.once="message"></my-component>
  8. </div>
  • 注意:若传递引用类型,则无论哪种绑定类型,在子组件修改 props 都会影响父组件的状态。

props 类型验证

  1. Vue.component('my-component', {
  2. props: {
  3. // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
  4. propA: Number,
  5. // 多个可能的类型
  6. propB: [String, Number],
  7. // 必填的字符串
  8. propC: {
  9. type: String,
  10. required: true
  11. },
  12. // 带有默认值的数字
  13. propD: {
  14. type: Number,
  15. default: 100
  16. },
  17. // 带有默认值的对象
  18. propE: {
  19. type: Object,
  20. // 对象或数组默认值必须从一个工厂函数获取
  21. default: function () {
  22. return { message: 'hello' }
  23. }
  24. },
  25. // 自定义验证函数
  26. propF: {
  27. validator: function (value) {
  28. // 这个值必须匹配下列字符串中的一个
  29. return ['success', 'warning', 'danger'].indexOf(value) !== -1
  30. }
  31. }
  32. }
  33. })

五、父子组件间的事件通信(方法)

  • 使用事件绑定机制,父组件向子组件传递方法,绑定后,子组件可以通过某些方式来调用传递过来的这个方法了
  • 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;
  • 父组件将方法的引用传递给子组件,其中,getMsg是父组件中methods中定义的方法名称,func是子组件调用传递过来方法时候的方法名称
  1. <son v-on:func="show"></son>
  • 子组件内部通过this.$emit('方法名', 要传递的数据)方式,来调用父组件中的方法,同时把数据传递给父组件使用
  1. <div id="app">
  2. <h1>{{msg}}</h1>
  3. <!-- 父组件向子组件传递方法,使用的是事件绑定机制 -->
  4. <!-- v-on,当我们自定义了一个事件属性之后,那么子组件就能够通过某种方式来调用 -->
  5. <son @func="getMsg"></son>
  6. </div>
  7. <!-- 组件模板定义 -->
  8. <template id="son">
  9. <div>
  10. <h1>这是子组件</h1>
  11. <input type="button" value="向父组件传值" @click="sendMsg" />
  12. </div>
  13. </template>
  14. <script>
  15. const son = {
  16. template: '#son', // 组件模板Id
  17. data(){
  18. return {
  19. msg: "我是子组件传递过来的数据"
  20. }
  21. },
  22. methods: {
  23. sendMsg() { // 按钮的点击事件
  24. // 我们可以通过调用父组件传递过来的方法,将子组件数据通过参数形式传递给父组件中
  25. this.$emit('func', this.msg); // 调用父组件传递过来的方法,同时把数据传递出去
  26. }
  27. }
  28. }
  29. // 子组件的定义方式
  30. Vue.component('son',son);
  31. // 创建 Vue 实例,得到 ViewModel
  32. var vm = new Vue({
  33. el: '#app',
  34. data: {
  35. msg: "父组件"
  36. },
  37. methods: {
  38. getMsg(val){ // 子组件中,通过 this.$emit() 实际调用的方法,在此进行定义
  39. this.msg = val;
  40. console.log("调用了父组件身上的getMsg方法");
  41. alert(val);
  42. }
  43. }
  44. });
  45. </script>

5.1 $emit() 与 $on() 函数

  • vm.$emit( event, arg ) // 触发当前实例上的事件
  • vm.$on( event, fn ) // 监听 event 事件后运行 fn;
  • 用法:
    1. 父组件可以使用 props 把数据传给子组件。
    2. 子组件可以使用 $emit 触发父组件的自定义事件。

六、非父子组件事件通信

6.1 父子组件访问

  • $parent:访问当前组件的父组件
  • $root:访问当前主键的根组件

6.2 ref 获取DOM元素和组件

  • 我们可以在元素上使用ref属性,然后赋予一个优雅的名字
  1. <h3 ref="myh3">我是H3</h3>
  2. <!-- login组件 -->
  3. <login ref="login"></login>
  • 在实例中,我们通过$refs来获取DOM
  1. getElement() { console.log(this.$refs.myh3); console.log(this.$refs.login.$el.innerText);}

七、组件的应用案例

7.1 分页组件

  1. <style>
  2. .pagination {
  3. display: flex;
  4. list-style: none;
  5. }
  6. .page-item {
  7. width: 25px;
  8. height: 25px;
  9. line-height: 25px;
  10. text-align: center;
  11. border: 1px solid #ccc;
  12. }
  13. .page-item.active {
  14. background-color: skyblue;
  15. }
  16. </style>
  17. <div id="app">
  18. <page-component :total="total"></page-component>
  19. </div>
  20. <template id="page-component">
  21. <ul class="pagination">
  22. <li :class="p == page ? 'page-item active' : 'page-item'" v-for="p in pagecount">
  23. <a href="#" class="page-link" @click.prevent="page = p">{{ p }}</a>
  24. </li>
  25. </ul>
  26. </template>
  27. <script>
  28. const pageComponent = {
  29. template: '#page-component',
  30. name: 'PageComponent',
  31. props: ['total'],
  32. data: function() {
  33. return {
  34. page: 1, // 当前页码
  35. pagesize: 10 // 每页显示条数
  36. }
  37. },
  38. computed: {
  39. pagecount: function() {
  40. // 总页码数
  41. return Math.ceil(this.total / this.pagesize)
  42. }
  43. }
  44. }
  45. var vm = new Vue({
  46. el: '#app',
  47. data: {
  48. total: 35
  49. },
  50. components: {
  51. pageComponent
  52. }
  53. })
  54. </script>

7.2 购物车组件

  1. <div id="app">
  2. <div v-for="goods in goodslist">
  3. <p>商品名称:{{ goods.name }}</p>
  4. <p>单价:{{ goods.price }}</p>
  5. <cart-component v-model="goods.count"></cart-component>
  6. <hr>
  7. </div>
  8. <div>
  9. 订单总金额:{{ amount }} 元
  10. </div>
  11. </div>
  12. <template id="cart-component">
  13. <div class="cart">
  14. <button @click="count--; updateCount();">-</button>
  15. <input type="text" v-model="count" style="width: 50%;" @input="updateCount()">
  16. <button @click="count++; updateCount();">+</button>
  17. </div>
  18. </template>
  19. <script>
  20. const cartComponent = {
  21. name: 'Cart',
  22. template: '#cart-component',
  23. // 在组件中不允许直接修改 props 中的数据
  24. props: ['value'],
  25. data: function() {
  26. return {
  27. count: this.value
  28. }
  29. },
  30. methods: {
  31. // v-model 指令双向数据绑定,修改父组件内容
  32. updateCount: function() {
  33. // 触发 input 事件
  34. this.$emit('input', this.count)
  35. }
  36. }
  37. }
  38. const app = new Vue({
  39. el: '#app',
  40. data: {
  41. goodslist: [
  42. {
  43. name: 'iphone 8 plus',
  44. price: 5888,
  45. count: 0
  46. },
  47. {
  48. name: 'iphone x',
  49. price: 7222,
  50. count: 0
  51. }
  52. ]
  53. },
  54. computed: {
  55. // 当前订单总金额
  56. amount: function() {
  57. var money = 0;
  58. this.goodslist.forEach(goods => {
  59. money += parseInt(goods.count) * parseInt(goods.price)
  60. })
  61. return money;
  62. }
  63. },
  64. components: {
  65. cartComponent
  66. }
  67. })
  68. </script>

7.3 评论列表案例

  1. <div id="app">
  2. <cmt-box @loadcomments="loadComments"></cmt-box>
  3. <ul class="list-group">
  4. <li class="list-group-item" v-for="item in list" :key="item.id">
  5. <span class="badge">评论人:{{item.user}}</span> {{item.content}}
  6. </li>
  7. </ul>
  8. </div>
  9. <template id="temp">
  10. <div>
  11. <div class="form-group">
  12. <label for="">评论人:</label>
  13. <input type="text" class="form-control" v-model="user">
  14. </div>
  15. <div class="form-group">
  16. <label for="">评论内容:</label>
  17. <textarea class="form-control" v-model="content"></textarea>
  18. </div>
  19. <div class="form-group">
  20. <input type="button" value="发表评论" @click="postComment">
  21. </div>
  22. </div>
  23. </template>
  24. <script>
  25. const vm = new Vue({
  26. el: '#app',
  27. data: {
  28. list: []
  29. },
  30. methods: {
  31. loadComments() {
  32. const list = JSON.parse(localStorage.getItem('cmts') || '[]');
  33. this.list = list;
  34. console.log(6666);
  35. }
  36. },
  37. created() {
  38. this.loadComments();
  39. },
  40. components: {
  41. 'cmt-box': {
  42. template: '#temp',
  43. data() {
  44. return {
  45. user: '',
  46. content: ''
  47. }
  48. },
  49. methods: {
  50. postComment() {
  51. const comment = {
  52. id: Date.now(),
  53. user: this.user,
  54. content: this.content
  55. };
  56. // 从localStorage中获取所有的评论
  57. const list = JSON.parse(localStorage.getItem('cmts') || '[]');
  58. list.unshift(comment);
  59. // 保存最新的评论数据
  60. localStorage.setItem('cmts', JSON.stringify(list));
  61. this.user = this.content = '';
  62. this.$emit('loadcomments');
  63. }
  64. },
  65. }
  66. }
  67. });
  68. </script>

更多相关文章

  1. vue api实例方法 有四个 $watch $emit $forceUpdate $nextTick!
  2. React转微信小程序:构思
  3. Hyperloop,让发布简洁高效
  4. Kubernetes集群组件的安全
  5. 组件化思维对于一个UI设计来说有多重要?
  6. 谈谈使用JS库解决小程序跨页传递消息和数据问题的方法
  7. Django 使用正则匹配URL 并将匹配成功的值传递给视图函数
  8. 【Vue框架学习】过滤器、自定义指令、生命周期、动画、组件、路
  9. Vue组件的注册与挂载流程,实例演示; 2. 路由原理与注册流程,实例

随机推荐

  1. 关于结构化、半结构化、非结构化问题
  2. HTML5 3D爱心动画 晚来的七夕礼物
  3. 垂直对齐div中的两个元素
  4. 为什么在vs里使用css时会说FILTER非已知
  5. 使用shell从标记中提取多个属性
  6. 解决IE9以下版本浏览器不支持HTML5标签的
  7. HTML文档是什么意思?
  8. HTML5游戏的即时性
  9. 基于HTML5 Canvas WebGL制作分离摩托车
  10. 使三个标签填充容器的宽度