【vue】8.0 指令、过滤器、传送门teleport、render函数、插件

自定义指令directives

对底层dom进行操作的封装,目前封装一个自定义指令,作用是:谁用这个指令就会变成对应的颜色。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="" rel="stylesheet">
<script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
    <div id="box">
      <!-- v-if="isShow" -->
      <div v-hello="'red'">11111111</div>

      <div v-hello="'yellow'">22222222</div>

      <div v-hello="color">333333333333333</div>
    </div>  

    <script type="text/javascript">
      
    // Vue.compoennt("aa",{}) 
      // directive指令 - dom 操作
      // 
      Vue.directive("hello",{
        //指令的生命周期-第一次插入节点调用
          inserted(el,binding,vnode){
            // vnode ,vdom, virtual node,  虚拟节点 虚拟dom
            console.log("此时dom节点创建",vnode)
            el.style.backgroundColor = binding.value;
          },

          update(el,binding){
            console.log("此时绑定的状态改变时会执行")
            el.style.backgroundColor = binding.value;
          }
      })

      var vm = new Vue({
        el:"#box",
        data:{
          title:"1111111111111111",
          color:'blue'
        },
        // directives
      })
       
    </script>
</body>
</html>

bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:卸载的时候会执行。

指令钩子函数会被传入以下参数:
inserted(el,bind,newVnode,oldVnode)

  • el:指令所绑定的元素,可以用来直接操作 DOM。
  • binding:一个对象,包含以下 property:
    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2
    • oldValue:指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"
    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
  • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
  • oldVnode:上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用。
指令函数的简写
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="" rel="stylesheet">
<script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
    <div id="box">
      <!-- v-if="isShow" -->
      <div v-hello="'red'">11111111</div>

      <div v-hello="'yellow'">22222222</div>

      <div v-hello="color">333333333333333</div>
    </div>  

    <script type="text/javascript">
      
    // Vue.compoennt("aa",{}) 
      // directive指令 - dom 操作
      // 
      Vue.directive("hello",function(el,binding,vnode){
        el.style.backgroundColor = binding.value
      })

      var vm = new Vue({
        el:"#box",
        data:{
          title:"1111111111111111",
          color:'red'
        },
        // directives
      })
       
    </script>
</body>
</html>

再举一个例子:

<script>
  // 自定义指令 directive

  const app = Vue.createApp({
    data() {
      return {
        distance: 110
      }
    },
    template: `
      <div>
        <div v-pos:right="distance" class="header">
          <input />
        </div>
      </div>
    `
  });

  app.directive('pos', (el, binding) => {
    el.style[binding.arg] = (binding.value + 'px');
  })

  const vm = app.mount('#root');
</script>

也可以如下编写:


image.png
指令轮播
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">

<link rel="stylesheet" href="lib/swiper/css/swiper.css">
<script src="lib/swiper/js/swiper.js"></script>
<script type="text/javascript" src="lib/vue.js"></script>

<style>
  .swiper-container {
      width: 600px;
      height: 300px;
  }  
</style>
</head>
<body>
  <div id="box">
    <div class="swiper-container">
        <div class="swiper-wrapper">
            <div class="swiper-slide" v-for="data,index in list" v-swipe="index">
              {{data}}
            </div>
        </div>
        <!-- 如果需要分页器 -->
        <div class="swiper-pagination"></div>
        
        <!-- 如果需要导航按钮 -->
        <!-- <div class="swiper-button-prev"></div> -->
        <!-- <div class="swiper-button-next"></div> -->
        
        <!-- 如果需要滚动条 -->
        <!-- <div class="swiper-scrollbar"></div> -->
    </div>

  </div>

    <script type="text/javascript">
    
      Vue.directive("swipe",{
        inserted(el,binding,vnode){
          console.log(binding.value);

          if(binding.value === vnode.context.list.length-1){
            var mySwiper = new Swiper ('.swiper-container', {
                // direction: 'vertical', // 垂直切换选项
                  // 如果需要分页器
                pagination: {
                  el: '.swiper-pagination',
                }
            })
          }
        }
      })


      new Vue({
        el:"#box",
        data:{
          list:[]
        },

        mounted(){

          setTimeout(() => {
            this.list = ["111","2222","3333"];

            console.log("节点创建完了????","没有,异步渲染")
            


          }, 2000)
        },

        updated(){
          // console.log("节点创建完了????","更新阶段渲染")
          // var mySwiper = new Swiper ('.swiper-container', {
          //     // direction: 'vertical', // 垂直切换选项
          //       // 如果需要分页器
          //     pagination: {
          //       el: '.swiper-pagination',
          //     }
          // })
        }
      })
 </script>
</body>
</html>
轮播-nextTick

直接解决(避免)了swiper初始化过早的问题。并且防止了多次反复执行指令勾子函数更新的问题。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">

<link rel="stylesheet" href="lib/swiper/css/swiper.css">
<script src="lib/swiper/js/swiper.js"></script>
<script type="text/javascript" src="lib/vue.js"></script>

<style>
  .swiper-container {
      width: 600px;
      height: 300px;
  }  
</style>
</head>
<body>
  <div id="box">
    <div class="swiper-container">
        <div class="swiper-wrapper">
            <div class="swiper-slide" v-for="data,index in list">
              {{data}}
            </div>
        </div>
        <!-- 如果需要分页器 -->
        <div class="swiper-pagination"></div>

    </div>

  </div>

    <script type="text/javascript">
      



      new Vue({
        el:"#box",
        data:{
          list:[]
        },

        mounted(){

          setTimeout(() => {
            this.list = ["111","2222","3333"];

            // 黑魔法
            this.$nextTick(()=>{
              console.log("我待会才会执行")
              
              var mySwiper = new Swiper ('.swiper-container', {
                  // direction: 'vertical', // 垂直切换选项
                    // 如果需要分页器
                  pagination: {
                    el: '.swiper-pagination',
                  }
              })
            })
            console.log("节点创建完了????","没有,异步渲染")
          }, 2000)
        },

        updated(){
          console.log("updated");
        }
      })
     
    </script>
</body>
</html>
过滤器

Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。

首选准备一个加载数据json文件。

{
    "coming": [],
    "movieIds": [248172, 1218727, 346629, 1228776, 1239544, 672279, 1211727, 1230152, 1211412, 672379, 1205909, 1234116,
        883196, 1243904, 1207260, 1263355, 1239281, 476263, 1212492, 1217701, 1237437, 1162868, 346765, 1229702,
        1239844, 337896, 1216383, 507792, 1167831, 1206939, 248123, 245881, 1206415
    ],
    "stid": "576591972453269000",
    "stids": [{
        "movieId": 248172,
        "stid": "576591972453269000_a248172_c0"
    }, {
        "movieId": 1218727,
        "stid": "576591972453269000_a1218727_c1"
    }, {
        "movieId": 346629,
        "stid": "576591972453269000_a346629_c2"
    }, {
        "movieId": 1228776,
        "stid": "576591972453269000_a1228776_c3"
    }, {
        "movieId": 1239544,
        "stid": "576591972453269000_a1239544_c4"
    }, {
        "movieId": 672279,
        "stid": "576591972453269000_a672279_c5"
    }, {
        "movieId": 1211727,
        "stid": "576591972453269000_a1211727_c6"
    }, {
        "movieId": 1230152,
        "stid": "576591972453269000_a1230152_c7"
    }, {
        "movieId": 1211412,
        "stid": "576591972453269000_a1211412_c8"
    }, {
        "movieId": 672379,
        "stid": "576591972453269000_a672379_c9"
    }, {
        "movieId": 1205909,
        "stid": "576591972453269000_a1205909_c10"
    }, {
        "movieId": 1234116,
        "stid": "576591972453269000_a1234116_c11"
    }],
    "total": 33,
    "movieList": [{
        "id": 248172,
        "haspromotionTag": false,
        "img":"http://128.180/xxxxxx",
        "version": "v3d imax",
        "nm": "复仇者联盟4:终局之战",
        "preShow": false,
        "sc": 9.1,
        "globalReleased": true,
        "wish": 1849927,
        "star":"王麻子",
        "rt": "2019-04-24",
        "showInfo": "今天107家影院放映3177场",
        "showst": 3,
        "wishst": 0
    }, {
        "id": 1218727,
        "haspromotionTag": false,
        "img":"http://128.180/xxxxxx",
        "version": "",
        "nm": "何以为家",
        "preShow": false,
        "sc": 0,
        "globalReleased": false,
        "wish": 94818,
        "star":"王麻子",
        "rt": "2019-04-29",
        "showInfo": "2019-04-29 下周一上映",
        "showst": 4,
        "wishst": 0
    }, {
        "id": 346629,
        "haspromotionTag": false,
        "img":"http://w.h/xxxxxx",
        "version": "v3d imax",
        "nm": "大侦探皮卡丘",
        "preShow": false,
        "sc": 0,
        "globalReleased": false,
        "wish": 181370,
        "star":"王麻子",
        "rt": "2019-05-10",
        "showInfo": "2019-05-10上映",
        "showst": 4,
        "wishst": 0
    }, {
        "id": 1228776,
        "haspromotionTag": false,
        "img":"http://w.h/xxxxxx",
        "version": "",
        "nm": "下一任:前任",
        "preShow": false,
        "sc": 0,
        "globalReleased": false,
        "wish": 284385,
        "star":"王麻子",
        "rt": "2019-05-01",
        "showInfo": "2019-05-01 下周三上映",
        "showst": 4,
        "wishst": 0
    }, {
        "id": 1239544,
        "haspromotionTag": false,
        "img":"http://128.180/xxxxxx",
        "version": "",
        "nm": "调音师",
        "preShow": false,
        "sc": 9.1,
        "globalReleased": true,
        "wish": 14608,
        "star":"王麻子",
        "rt": "2019-04-03",
        "showInfo": "今天60家影院放映155场",
        "showst": 3,
        "wishst": 0
    }, {
        "id": 672279,
        "haspromotionTag": false,
        "img":"http://128.180/xxxxxx",
        "version": "",
        "nm": "雪暴",
        "preShow": false,
        "sc": 0,
        "globalReleased": false,
        "wish": 24301,
        "star":"王麻子",
        "rt": "2019-04-30",
        "showInfo": "2019-04-30 下周二上映",
        "showst": 4,
        "wishst": 0
    }, {
        "id": 1211727,
        "haspromotionTag": false,
        "img":"http://128.180/xxxxxx",
        "version": "",
        "nm": "反贪风暴4",
        "preShow": false,
        "sc": 9.1,
        "globalReleased": true,
        "wish": 161080,
        "star":"王麻子",
        "rt": "2019-04-04",
        "showInfo": "今天55家影院放映129场",
        "showst": 3,
        "wishst": 0
    }, {
        "id": 1230152,
        "haspromotionTag": false,
        "img":"http://128.180/xxxxxx",
        "version": "",
        "nm": "撞死了一只羊",
        "preShow": false,
        "sc": 8,
        "globalReleased": true,
        "wish": 8422,
        "star":"王麻子",
        "rt": "2019-04-26",
        "showInfo": "今天38家影院放映81场",
        "showst": 3,
        "wishst": 0
    }, {
        "id": 1211412,
        "haspromotionTag": false,
        "img":"http://128.180/xxxxxx",
        "version": "v3d",
        "nm": "神奇乐园历险记",
        "preShow": false,
        "sc": 8.8,
        "globalReleased": true,
        "wish": 4779,
        "star":"王麻子",
        "rt": "2019-04-19",
        "showInfo": "今天39家影院放映63场",
        "showst": 3,
        "wishst": 0
    }, {
        "id": 672379,
        "haspromotionTag": false,
        "img":"http://128.180/xxxxxx",
        "version": "v3d",
        "nm": "悟空奇遇记",
        "preShow": false,
        "sc": 0,
        "globalReleased": false,
        "wish": 11472,
        "star":"王麻子",
        "rt": "2019-05-01",
        "showInfo": "2019-05-01 下周三上映",
        "showst": 4,
        "wishst": 0
    }, {
        "id": 1205909,
        "haspromotionTag": false,
        "img":"http://128.180/xxxxxx",
        "version": "",
        "nm": "祈祷落幕时",
        "preShow": false,
        "sc": 9,
        "globalReleased": true,
        "wish": 11057,
        "star":"王麻子",
        "rt": "2019-04-12",
        "showInfo": "今天16家影院放映40场",
        "showst": 3,
        "wishst": 0
    }, {
        "id": 1234116,
        "haspromotionTag": false,
        "img":"http://128.180/xxxxxx",
        "version": "",
        "nm": "猫公主苏菲",
        "preShow": false,
        "sc": 0,
        "globalReleased": false,
        "wish": 6533,
        "star":"王麻子",
        "rt": "2019-05-01",
        "showInfo": "2019-05-01 下周三上映",
        "showst": 4,
        "wishst": 0
    }]
}  
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="" rel="stylesheet">
<style>

</style>

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>

    <!-- <h1 class="animated hinge infinite">1111111111111</h1> -->

    <div id="box">
       <ul>
         <li v-for="data in datalist">
            {{data.nm}}
           <!--  <img :src="handlePath(data.img)"/> -->
           <img :src="data.img | kerwinpath"/>

           {{ data.img | kerwinpath }}
         </li>
       </ul>
    </div>

    <script>

      //过滤器 ---- 数据格式化
      Vue.filter("kerwinpath",function(path){
        return path.replace('w.h','128.180')
      })    


      var vm = new Vue({
        el:"#box",
        data:{
          isShow:true,
          datalist:[]
        },

        mounted(){
          axios.get("test.json").then(res=>{
            console.log(res.data);
            this.datalist = res.data.movieList
          })
        },

        methods:{
          handlePath(path){
            console.log(path);
            return path.replace('w.h','128.180');
          }
        }
      })
    </script>
</body>
</html>

过滤器其实就是一种函数写法,把第一个参数写到|符号前面,如果还有其他参数,就写到函数名的后面。只不过这个函数方法必须写到filers中。

过滤器函数总接收表达式的值 (之前的操作链的结果) 作为第一个参数。在上述例子中,capitalize 过滤器函数将会收到 message 的值作为第一个参数。

过滤器可以串联:

{{ message | filterA | filterB }}
在这个例子中,filterA 被定义为接收单个参数的过滤器函数,表达式 message 的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB,将 filterA 的结果传递到 filterB 中。
相当于:
function filterB (function filterA (message ))
过滤器是 JavaScript 函数,因此可以接收参数:

{{ message | filterA('arg1', arg2) }}
这里,filterA 被定义为接收三个参数的过滤器函数。其中 message 的值作为第一个参数,普通字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数。
相当于:
function filterA(message ,'arg1',arg2)

传送门Teleport

如下例子,比如想点击按钮给页面增加一个蒙层。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>lesson 30</title>
        <style>
            .area {
                position: absolute;
                left: 50%;
                top: 50%;
                transform: translate(-50%, -50%);
                width: 200px;
                height: 300px;
                background: green;
            }

            .mask {
                position: absolute;
                left: 0;
                right: 0;
                top: 0;
                bottom: 0;
                background: #000;
                opacity: 0.5;
                color: #fff;
                font-size: 100px;
            }
        </style>
        <script src="https://unpkg.com/vue@next"></script>
    </head>
    <body>
        <div id="root"></div>
    </body>
    <script>
        // teleport 传送门
        const app = Vue.createApp({
            data() {
                return {
                    show: false,
                }
            },
            methods: {
                handleBtnClick() {
                    this.show = !this.show;
                }
            },
            template: `
      <div class="area">
        <button @click="handleBtnClick">按钮</button>
          <div class="mask" v-show="show"></div>
      </div>
    `
        });

        const vm = app.mount('#root');
    </script>
</html>

点击按钮出现如下情况:

image.png

因为在css样式mask中,absolute绝对定位是相对父元素的,所以遮罩和上下左右移动都不会超出area标签的范围。如果想要在组件内实现全遮罩的效果,这时候就需要用到传送门Teleport:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>lesson 30</title>
  <style>
    .area {
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      width: 200px;
      height: 300px;
      background: green;
    }
    .mask {
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      background: #000;
      opacity: 0.5;
      color: #fff;
      font-size: 100px;
    }
  </style>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="root"></div>
  <div id="hello"></div>
</body>
<script>
  // teleport 传送门
  const app = Vue.createApp({
    data() {
      return {
        show: false,
        message: 'hello'
      }
    },
    methods: {
      handleBtnClick() {
        this.show = !this.show;
      }
    },
    template: `
      <div class="area">
        <button @click="handleBtnClick">按钮</button>
        <teleport to="#hello">
          <div class="mask" v-show="show">{{message}}</div>
        </teleport>
      </div>
    `
  });

  const vm = app.mount('#root');
</script>
</html>

效果如下:


image.png

render函数

如下代码:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>lesson 31</title>
        <script src="https://unpkg.com/vue@next"></script>
    </head>
    <body>
        <div id="root"></div>
    </body>
    <script>
        // render function
        // template -> render -> h -> 虚拟DOM(JS对象)-> 真实 DOM -> 展示到页面上
        const app = Vue.createApp({
            template: `
      <my-title :level="2">
        hello dell
      </my-title>
    `
        });

        app.component('my-title', {
            props: ['level'],
            template: `
            <h1 v-if="level===1"><slot/></h1>
            <h2 v-if="level===2"><slot/></h2>
            <h3 v-if="level===3"><slot/></h3>
            <h4 v-if="level===4"><slot/></h4>
            `
        })

        const vm = app.mount('#root');
    </script>
</html>

如何把如上代码写得更加优雅,修改组件代码:

render function
template -> render -> h -> 虚拟DOM(JS对象)-> 真实 DOM -> 展示到页面上

  app.component('my-title', {
    props: ['level'],
    render() {
      //创建一个vue的h函数
      const { h } = Vue;
      //这是一个虚拟DOM。返回一个h函数的返回值,传递的第一个参数是标签名,第二个参数为标签的自定义或者自带的属性和属性值,第三个参数为具体标签内的内容。
      //这里通过`  this.$slot `获得各式各样的插槽,这里我们没有具体指向,想使用默认插槽,语法就是` this.$slots.default()`。
      //这里第三个参数可以写成一个数组,相当于往下无限地嵌套的js对象。
      return h('h' + this.level, {}, [
        this.$slots.default(),
        h('h4', {}, 'dell')
      ])
    }
  })

我们在组件中写的template 都会被编译成render。

插件

我们都知道vue有很多第三方插件,那么插件的基本原理是什么?
mixin可以对代码进行很好的封装,但是使用插件的功能,能进行更好的封装。
比如定义一个myPlugin的插件:
一定要定义一个install方法,这是插件走的一个必备逻辑。

  const myPlugin = {
    //支持2个参数:app:根实例,有这个可扩展任何想要的实例。
        //options:额外的参数会传到options中
    install(app, options) {
      })

使用插件:

  // plugin 插件, 也是把通用性的功能封装起来
  const myPlugin = {
    //支持2个参数:app,跟实例,有这个可扩展任何想要的实例。
        //options:额外的参数会传到options中
    install(app, options) {
      })
      })
    }
  }

  const app = Vue.createApp({
    template: `
      <my-title />
    `
  });

  app.component('my-title', {
  
    template: `<div>hello world</div>`
  })

  app.use(myPlugin, { name: 'dell'});

既然得到app的实例,那么可以进行很多操作,比如设置全局变量:

  // plugin 插件, 也是把通用性的功能封装起来
  const myPlugin = {
    //支持2个参数:app,跟实例,有这个可扩展任何想要的实例。
        //options:额外的参数会传到options中
    install(app, options) {
       app.provide('name', 'Dell Lee');
      })
      })
    }
  }

  const app = Vue.createApp({
    template: `
      <my-title />
    `

  });

  app.component('my-title', {
      inject: ['name'],
    template: `<div>{{name}}</div>`
  })

  app.use(myPlugin, { name: 'dell'});

也可以扩展自定义指令:

  // plugin 插件, 也是把通用性的功能封装起来
  const myPlugin = {
    //支持2个参数:app,跟实例,有这个可扩展任何想要的实例。
        //options:额外的参数会传到options中
    install(app, options) {
       app.provide('name', 'Dell Lee');
      })
      app.directive('focus', {
        mounted(el) {
          el.focus();
        }
      })
      })
    }
  }

  const app = Vue.createApp({
    template: `
      <my-title />
    `

  });

  app.component('my-title', {
      inject: ['name'],
    template: `<div>{{name}}<input v-focus /></div>`
  })

  app.use(myPlugin, { name: 'dell'});

也可以扩展其他内容:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>lesson 32</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="root"></div>
</body>
<script>
  // plugin 插件, 也是把通用性的功能封装起来
  const myPlugin = {
    //支持2个参数:app,跟实例,有这个可扩展任何想要的实例。
        //options:额外的参数会传到options中
    install(app, options) {
      app.provide('name', 'Dell Lee');
      app.directive('focus', {
        mounted(el) {
          el.focus();
        }
      })
      app.mixin({
        mounted(){
          console.log('mixin')
        }
      })
      app.config.globalProperties.$sayHello = 'hello world';
    }
  }

  const app = Vue.createApp({
    template: `
      <my-title />
    `
  });

  app.component('my-title', {
    inject: ['name'],
    mounted() {
      console.log(this.$sayHello);
    },
    template: `<div>{{name}}<input v-focus /></div>`
  })

  app.use(myPlugin, { name: 'dell'});

  const vm = app.mount('#root');
</script>
</html>

对数据做校验的插件编写

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>lesson 33</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="root"></div>
</body>
<script>
  // 对数据做校验的插件
  const app = Vue.createApp({
    data() {
      return { name: 'dell', age: 23}
    },
    rules: {
      age: {
        validate: age => age > 25,
        message: 'too young, to simple'
      },
      name: {
        validate: name => name.length >= 4,
        message: 'name too short'
      }
    },
    template: `
      <div>name:{{name}}, age:{{age}}</div>
    `
  });

  const validatorPlugin = (app, options) => {
    app.mixin({
      created() {
        for(let key in this.$options.rules) {
          const item = this.$options.rules[key];
          this.$watch(key, (value) => {
            const result = item.validate(value);
            if(!result) console.log(item.message);
          })
        }
      }
    })
  }

  app.use(validatorPlugin);
  const vm = app.mount('#root');
</script>
</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352

推荐阅读更多精彩内容

  • 很久没有更新,先聊一聊近况吧,5月底从上家公司离职了(任职时长两个多月),然后休息了几天入职了现在这家公司,离职的...
    还在努力的乌贼阅读 710评论 1 0
  • 此笔记关于filter的官方文档的一些讲解及个人的一些拓展 简单介绍一下过滤器,顾名思义,过滤就是一个数据经过了这...
    chenjieyi阅读 173,946评论 21 62
  • 过滤器函数总接收表达式的值 (之前的操作链的结果) 作为第一个参数。在上述例子中,capitalize过滤器函数将...
    easy_mark阅读 152评论 0 0
  • 根据Vue 官方给出的解释,过滤器是可被用于一些常见的文本格式化的东西。过滤器可以在两个地方被使用,一个是在双花括...
    虚竹_d36e阅读 211评论 0 0
  • 今天青石的票圈出镜率最高的,莫过于张艺谋的新片终于定档了。 一张满溢着水墨风的海报一次次的出现在票圈里,也就是老谋...
    青石电影阅读 10,325评论 1 2