Vue 插件写法

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

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】-- 模块热替换vue

2.vue-router

vue的路由仍是很方便的,比ag1的时候要方便不少。这种方便体如今三个方面:node

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

复制代码
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,若是导航分类多,对应策略是嵌套路由。git

2个是能够具体到元素了:es6

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

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

3个是事件拦截:web

若是咱们要作验证,最好不过的就是在用户到达页面以前进行验证:vue-router

复制代码
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入坑随记(一)-- 初始全家桶

相关文章
相关标签/搜索