zoukankan      html  css  js  c++  java
  • Vue 插件写法

    都说Vue2简单,上手容易,但小马过河,自己试了才晓得,除了ES6语法和webpack的配置让你感到陌生,重要的是思路的变换,以前随便拿全局变量和修改dom的锤子不能用了,变换到关注数据本身。vue的官方文档还是不错的,由浅到深,但是当你用vue-cli构建一个工程的时候,发现官方文档还是不够用,得参考git上开源的项目并去学习es6。而且vue的全家桶(vue-cli,vue-router,vue-resource,vuex)还是都要上的。

    1.vue-cli

    这个构建工具大大降低了webpack的使用难度,支持热更新,有webpack-dev-server的支持,相当于启动了一个请求服务器,给你搭建了一个测试环境,你只关注开发就好。

    复制代码
    # 全局安装 vue-cli
    $ npm install --global vue-cli
    # 创建一个基于 webpack 模板的新项目
    $ vue init webpack my-project
    # 安装依赖,走你
    $ cd my-project
    $ npm install
    $ npm run dev
    复制代码

    热更新的机制是检测文件的变化并用websocket通知客户端做出相应的更新。详细的可以移步:【webpack】-- 模块热替换

    2.vue-router

    vue的路由还是很方便的,比ag1的时候要方便很多。这种方便体现在三个方面:

    1个是路由和页面(组件)对应:

    复制代码
    import Vue from 'vue'
    import Router from 'vue-router'
    import Home from '@/components/Home'
    import Chat from '@/components/Chat'
    import Firends from '@/components/Firends'
    import Logon from '@/components/Logon'
    
    Vue.use(Router)
    let router=new Router({
      routes: [
        {
          path: '/home',
          name: 'Home',
          component: Home
        },
         { path: '/', redirect: '/home' },//重定向
        {
          path: '/chat',
          name: 'Chat',
          component: Chat
        },
        {
          path: '/firends',
          name: 'Firends',
          component: Firends
        },
        {
          path: '/logon',
          name: 'Logon',
          component: Logon
        }
      ]
    });
    复制代码

    常人的思路就是这样,而ag1中还需要带上控制器(vue中没有这个概念,取而代之的是关注组件就行),这样用起来更简单。MVC模式中,需要指到controller下的action,如果导航分类多,对应策略是嵌套路由。

    2个是可以具体到元素了:

    <router-link    class="footer-item"  exact  to="home">首页</router-link>

    这个to后面的home(忽略大小写)就是上面定义的路由名称。这样就很方便了。类似于Asp.net MVC 的路由可以用名称来渲染出路径,而不用什么时候都要输入路径。

    3个是事件拦截:

    如果我们要做验证,最好不过的就是在用户到达页面之前进行验证:

    复制代码
    router.beforeEach((to, from, next) => {
      //todo 以后增加不需要验证的地址判断
      if(to.path!=="/logon"&&!store.state.userInfo.Account){
         next({ path: '/logon' })
         return;
      }else{
       next();
      } 
    })
    复制代码

    比如在beforeEach中进行处理。它还有很多功能,就不一一枚举了,官方文档:http://router.vuejs.org/zh-cn/

    3.组件中使用组件

    看了几个移动UI库,一开始奇怪怎么没有footer组件,现在明白,路由都这么方便了,第三方ui没必要封装了footer,也不方便封装(因为依赖路由)。于是导航可能是你需要自己来写的第一个组件。

    比如定义了个Footer.vue
    复制代码
    <template>
        <footer class="footer">
         <router-link    class="footer-item"  exact  to="home">
         <span class="icon icon-home">
         </span>
         <label>首页</label>
         </router-link>
         <router-link    class="footer-item"   to="statistics">
          <span class="icon icon-stat">
          </span>
          <label>统计</label>
         </router-link>
           <router-link    class="footer-item"   to="more">
          <span class="icon icon-more">
          </span>
          <label>更多</label>
         </router-link>
      </footer>
    </template>

    <script>
    export default {
    name: 'VFooter'
    }
    </script>

    复制代码

    只在App.Vue中就需要引入

    复制代码
    import VFooter from './VFooter'
     
    export default {
    name: 'app',
    data () {
    return {
    msg: 'this is home'
    }
    },
    components:{VFooter}
    }
    复制代码

    然后在App.Vue中就可以使用了

    <VFooter></VFooter>
    如果是要注册到全局 就用先在main.js中引入
    import Footer from './components/VFooter'
    //
    Vue.component('VFooter', Footer)//写在构造函数之前

    到了这一步就可以愉快的组装自己的页面了。如果需要给页面加上滑动效果,可以加一个transition(位于app.vue):

      <transition  name="slide-in" mode="out-in">
        <router-view></router-view>
       </transition>
    复制代码
    .slide-out-enter-active,
    .slide-out-leave-active,
    .slide-in-enter-active,
    .slide-in-leave-active {
      will-change: transform;
      transition: all 400ms;
      position: absolute;
    }
    .slide-out-enter {
      opacity: 0;
       transform: translateX(-100%);
      
    }
    .slide-out-leave-active {
      opacity: 0;
       transform: translateX(100%);
      
    }
    .slide-in-enter {
      opacity: 0;
       transform: translateX(100%);
    
    }
    .slide-in-leave-active {
      opacity: 0;
       transform: translateX(-100%);
    复制代码

    如何做到左右切换呢?

    4.vue-resource

    页面敲好了,得能发请求。https://github.com/pagekit/vue-resource 而前端如果不是自己写的接口首先关心的还是怎么设置代理,这个位于config/index.js下面的proxyTable。

    复制代码
        proxyTable: {
        '/api':
         {
               target: 'http://11.111.249.12:11',
               changeOrigin: true,
               pathRewrite:
               {
                  '^/api': ''
                }
        },
    复制代码

    跨域的设置不能少了changeOrigin。另外需要注意最后的这个'^/api': ''。 要注意你选择的"/api"是不是原路径中的一部分,不然容易出错。

    然后还有一部分就是设置请求头(main.js):

    复制代码
    import VueResource from 'vue-resource'
    Vue.use(VueResource);
    Vue.http.interceptors.push(function(request, next) {// modify method
      //request.method = 'POST';
    
      // modify headers
          
      request.headers.set('token',“token”);// continue to next interceptor
      next();
    });
    复制代码

    5.vuex

    全家桶里面一开始最让我懵逼的就是这个vuex。这是个什么鬼,为什么需要这个。没有玩过React,不知道什么是状态管理。当你想用一个全局变量的时候,你发现之前的招都不灵了。比如设置一个登录状态

    复制代码
    let login=(name,pwd,success,fail)=>{
        Vue.http.post(loginUrl,{account:name,password:pwd}).then(res=>{
         //.... window.hasLogin=true;

    },res=>{ fire(fail,"请求失败"); }) }
    复制代码

    然后再首页显示出来:

    <div @click="toggle">{{hasLogin}}</div>
    复制代码
    data () {
        return {
          hasLogin:window.hasLogin
        }
      },
       methods:{
         toggle(){
           window.hasLogin=!window.hasLogin;
           console.log("clicked",window.hasLogin)
         }
       },
    复制代码

    你发现你登录之后确实显示了true,但怎么点击也不会切换false或true。

    需要再赋值一次:

    this.hasLogin=window.hasLogin;

    为什么呢?因为你自己定义的变量,根本不属于vue的model。也就是没有处理过geter和seter,虽然变量的值是变化了,但仍然无法改变界面上的状态。所以你需要一个状态管理,按照一定的机制把你的变量变换成vue内部的模型。这个东西就是vuex。因为约定比较多,略显复杂点,但是耐心看一下还是很容易接受的。它主要分四个部分,state,getters,mutations,actions。先定义一个user.js如下:

    state就是我们放共享变量的地方。比如下面的userInfo.

    复制代码
    import Vue from 'vue';
    import Vuex from 'vuex';
    Vue.use(Vuex);
    
    const userStore=new Vuex.Store({
       state:{
           userInfo:{
               userName:""
           }
       },
       getters:{
           getUserInfo(state){
               return state.userInfo;
           }
       },
       mutations:{
           setUserInfo(state,userInfo){
               state.userInfo=userInfo;
           }
       },
       actions:{
           setUserInfo({commit}){
               commit('setUserInfo');
           }
       }
    
    })
    
    
    export default userStore;
    复制代码

    而getters顾名思义就是获取接口,mutations(突变)这个词有点唬人,其实就是setters嘛。里面的方法自带state参数。而actions就是mutations的异步方法。然后再main.js中引用一下:

    复制代码
    import store from './store/user';
    //...
     new Vue({
      el: '#app',
      router,
      store,
      template: '<App/>',
      components: { App }
    })
    复制代码

    然后我们在设置或获取的时候就可以使用指定的方法:

    import store from '@/store/user';
    //...
    store.commit('setUserInfo',user)

     可以直接通过store.state获取变量,也可以通过getters接口:

     computed:{
       ...mapGetters({username:'getUserName'})
      },

    这三个点是es6中的扩展运算符。将一个数组转为用逗号分隔的参数序列。

    当然这些状态刷新之后就没有了,如果想要暂存下来,可以放到sessionStorage中:

    if (window.sessionStorage.user) {
        var json=JSON.parse(window.sessionStorage.user);
        store.commit('setSessionUser',json)
    }

    当然要在muations中放进去

    复制代码
      //个人信息
      setUserInfo(state,user)
    { state.userInfo=user; window.sessionStorage.user=JSON.stringify(state.userInfo); },
    复制代码

    官方文档: https://vuex.vuejs.org/zh-cn/

    小结:到这儿基本五脏俱全了,可以愉快的玩耍了。还有一个是对生命周期的理解。组件实例每次加载都会执行一遍,所以在生命周期的方法中做请求和绑定的时候要清楚这一点。

    其他:

    Demo
    UI:
     
     
     
     
     
     

    学习了Vue全家桶和一些UI基本够用了,但是用元素的方式使用组件还是不够灵活,比如我们需要通过js代码直接调用组件,而不是每次在页面上通过属性去控制组件的表现。下面讲一下如何定义动态组件。

     Vue.extend

     思路就是拿到组件的构造函数,这样我们就可以new了。而Vue.extend可以做到:https://cn.vuejs.org/v2/api/#Vue-extend

    复制代码
    // 创建构造器
    var Profile = Vue.extend({
      template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
      data: function () {
        return {
          firstName: 'Walter',
          lastName: 'White',
          alias: 'Heisenberg'
        }
      }
    })
    // 创建 Profile 实例,并挂载到一个元素上。
    new Profile().$mount('#mount-point')
    复制代码

    官方提供了这个示例,我们进行一下改造,做一个简单的消息提示框。

    动态组件实现

    创建一个vue文件。widgets/alert/src/main.vue

    复制代码
    <template>
     <transition name="el-message-fade">
    <div  v-show="visible" class="my-msg">{{message}}</div>
      </transition>
    </template>
    
    <script  >
        export default{
            data(){
               return{
                   message:'',
                   visible:true
               } 
            },
            methods:{
                close(){
                    setTimeout(()=>{
                         this.visible = false;
                    },2000)
                },
            },
            mounted() {
            this.close();
          }
        }
    </script>
    复制代码

    这是我们组件的构成。如果是第一节中,我们可以把他放到components对象中就可以用了,但是这儿我们要通过构造函数去创建它。再创建一个widgets/alert/src/main.js

    复制代码
    import Vue from 'vue';
    let MyMsgConstructor = Vue.extend(require('./main.vue'));
    
    let instance;
    
    var MyMsg=function(msg){
     instance= new MyMsgConstructor({
         data:{
             message:msg
    }})
    
    //如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用 vm.$mount() 手动地挂载一个未挂载的实例。
    instance.$mount();
     
     document.body.appendChild(instance.$el)
     return instance;
    }
    
    
    export default MyMsg;
    复制代码

    require('./main.vue')返回的是一个组件初始对象,对应Vue.extend( options )中的options,这个地方等价于下面的代码:

    import alert from './main.vue'
    let MyMsgConstructor = Vue.extend(alert);

    而MyMsgConstructor如下。

     参考源码中的this._init,会对参数进行合并,再按照生命周期运行:

    复制代码
      Vue.prototype._init = function (options) {
        ...// merge options
        if (options && options._isComponent) {
          // optimize internal component instantiation
          // since dynamic options merging is pretty slow, and none of the
          // internal component options needs special treatment.
          initInternalComponent(vm, options);
        } else {
          vm.$options = mergeOptions(
            resolveConstructorOptions(vm.constructor),
            options || {},
            vm
          );
        }
    // expose real self
        vm._self = vm;
        initLifecycle(vm);
        initEvents(vm);
        initRender(vm);
        callHook(vm, 'beforeCreate');
        initInjections(vm); // resolve injections before data/props
        initState(vm);
        initProvide(vm); // resolve provide after data/props
        callHook(vm, 'created');
    
        ...
    
        if (vm.$options.el) {
          vm.$mount(vm.$options.el);
        }
      };
    复制代码

    而调用$mount()是为了获得一个挂载实例。这个示例就是instance.$el。

    可以在构造方法中传入el对象(注意上面源码中的mark部分,也是进行了挂载vm.$mount(vm.$options.el),但是如果你没有传入el,new之后不会有$el对象的,就需要手动调用$mount()。这个方法可以直接传入元素id。

     instance= new MessageConstructor({
         el:".leftlist",
         data:{
             message:msg
    }})

     这个el不能直接写在vue文件中,会报错。接下来我们可以简单粗暴的将其设置为Vue对象。

    调用

    在main.js引入我们的组件:

    复制代码
    //..
    import VueResource from 'vue-resource'
    import MyMsg from './widgets/alert/src/main.js';
    //..
    //Vue.component("MyMsg", MyMsg);
    Vue.prototype.$mymsg = MyMsg;
    复制代码

    然后在页面上测试一下:

    复制代码
    <el-button type="primary" @click='test'>主要按钮</el-button>
    //..

     methods:{
      test(){
      this.$mymsg("hello vue");
      }
     }

    复制代码

     

    这样就实现了基本的传参。最好是在close方法中移除元素:

    复制代码
     close(){
        setTimeout(()=>{
           this.visible = false;
           this.$el.parentNode.removeChild(this.$el);
          },2000)
       },
    复制代码

     回调处理

    回调和传参大同小异,可以直接在构造函数中传入。先修改下main.vue中的close方法:

    复制代码
    export default{
            data(){
               return{
                   message:'',
                   visible:true
               } 
            },
            methods:{
                close(){
                    setTimeout(()=>{
                         this.visible = false;
                          this.$el.parentNode.removeChild(this.$el);
    
                    if (typeof this.onClose === 'function') {
                     this.onClose(this);
                    }
                    },2000)
                },
            },
            mounted() {
            this.close();
          }
        }
    复制代码

    如果存在onClose方法就执行这个回调。而在初始状态并没有这个方法。然后在main.js中可以传入

    复制代码
    var MyMsg=function(msg,callback){
    
     instance= new MyMsgConstructor({
         data:{
             message:msg
        },
        methods:{
            onClose:callback
        } 
    })
    复制代码

    这里的参数和原始参数是合并的关系,而不是覆盖。这个时候再调用的地方修改下,就可以执行回调了。

       test(){
          this.$mymsg("hello vue",()=>{
            console.log("closed..")
          });
        },

    你可以直接重写close方法,但这样不推荐,因为可能搞乱之前的逻辑且可能存在重复的编码。现在就灵活多了。

    统一管理

     如果随着自定义动态组件的增加,在main.js中逐个添加就显得很繁琐。所以这里我们可以让widgets提供一个统一的出口,日后也方便复用。在widgets下新建一个index.js 

    复制代码
    import MyMsg from './alert/src/main.js';
    
    const components = [MyMsg];
    
    let install =function(Vue){
      components.map(component => {
        Vue.component(component.name, component);
      });
    
     Vue.prototype.$mymsg = MyMsg;
    
    }
    
    if (typeof window !== 'undefined' && window.Vue) {
      install(window.Vue);
    };
    
    export default {
        install
    }
    复制代码

    在这里将所有自定义的组件通过Vue.component注册。最后export一个install方法就可以了。因为接下来要使用Vue.use

    安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法将被作为 Vue 的参数调用。

    也就是把所有的组件当插件提供:在main.js中加入下面的代码即可。

    复制代码
    ...
    import VueResource from 'vue-resource'
    import Widgets from './Widgets/index.js'
    
    ...
    Vue.use(Widgets)
    复制代码

    这样就很简洁了。

    小结: 通过Vue.extend和Vue.use的使用,我们自定义的组件更具有灵活性,而且结构很简明,基于此我们可以构建自己的UI库。以上来自于对Element源码的学习。

    widgets部分源码:http://files.cnblogs.com/files/stoneniqiu/widgets.zip

    vue2入坑随记(一)-- 初始全家桶

  • 相关阅读:
    信息爆炸时代,对待信息的三种方式
    Spring事务管理
    归并排序和快速排序的衍生问题
    Linux之Shell命令
    程序员找工作的干货经验
    css3 Transition动画执行时有可能会出现闪烁的bug
    布尔值
    null, undefined理解
    js文字的无缝滚动(上下)
    vue实现文字上下滚动
  • 原文地址:https://www.cnblogs.com/libin-1/p/6962246.html
Copyright © 2011-2022 走看看