VUX使用

作者:高天阳

邮箱:[email protected]

更改历史

* 2018-10-29        高天阳     整理文档、补充最佳实践
* 2018-10-22        高天阳     补充安装与使用、最佳实践
* 2018-10-15        高天阳     初始化文档

1 简介

VUX(读音[v’ju:z],同views)是基于WeUIVue(2.x)开发的移动端UI组件库,主要服务于微信页面。

基于webpack + vue-loader + vux可以快速开发移动端页面,配合vux-loader方便你在WeUI的基础上定制需要的样式。

vux-loader保证了组件按需使用,因此不用担心最终打包了整个VUX的组件库代码。

VUX并不完全依赖于WeUIVUXWeUI的基础上扩展了多个常用组件,但是尽量保持整体UI样式接近WeUI的设计规范。

VUX 并不是一个能解决所有场景的完美解决方案(实际上也没有一个方案能解决所有问题),也会出现某些bug或者某些特性不支持, 所以如果遇到问题麻烦及时不带情绪正确反馈,我们乐于及时解决描述详细方便重现的问题。

即使你不直接使用VUX组件代码, 你依然可以参考VUX代码来实现自己的组件库。如果一定程度上帮助到了你,那么维护这个项目也就有所意义。

提示

VUX 是库而非框架,虽然有专用的 vux-loader,但并不影响你自由地使用其他组件库或者工具库。

VUX 使用的 CSS 预处理工具是 less(同 WeUI),但(利益于 .vue 单文件组件的灵活性)这并不影响你使用 SASS 等其他预处理器。

用以表示该组件库时请使用大写名字 VUX,用在说明版本号时使用小写 [email protected]

在使用VUX之前

如果你刚从后端转到前端,可能会被目前前端(表面的)工程复杂度惊吓到,但是放心,使用vue-cli从模板创建项目可以快速开始编码、构建, 仅仅是几行简单的命令不是么?

在使用VUX之前需要你至少已经会使用Vue,同时需要你大概了解Node.jsnpmcnpmyarn这些东西。

建议Node.js版本在7.6.0以上。

相关工具


WeUI

VUX样式基于WeUI,但是你不必通过使用VUX来使用WeUI。 简单的页面你可以直接引入WeUI样式。详细请参考WeUI 文档

Vue

VUX基于Vue的组件库,意味着你需要有Vue的基础知识。

如果还没有了解过,建议先看看Vue官方文档

Webpack

如果你直接使用vux2模板,你可以暂时不用了解。当你需要自定义一些配置时自然就能很快了解了。

Webpack 文档

vue-cli

Vue 官方用于快速创建项目的工具。

npm install vue-cli -g

或者使用 yarn

yarn global add vue-cli

vue-cli 文档

vue-loader

webpack loader,用于编译.vue文件,官方模板已经帮你配置好。

vue-loader 文档

vux-loader

VUX组件库的webpack loader,实现按需加载等功能。它不是替代vue-loader而是配合vue-loader使用。 如果你使用vux2模板,暂不需要手动使用它。

2 安装和使用

2.1 安装

如果你从没使用过 VUX,请参考 快速入门。

不推荐使用 umd 方式引用组件,但是如果不得不使用,可以参考 umd 构建

直接安装或者更新:

npm install vux --save

或者使用yarn

yarn add vux // 安装
yarn upgrade vux // 更新

如果你想直接从Github安装,请指定 v2 分支

npm install git://github.com/airyland/vux.git#v2

如果你是从0.x更新,请参考: 更新到2.x

vux2必须配合vux-loader使用, 请在build/webpack.base.conf.js里参照如下代码进行配置:

const vuxLoader = require('vux-loader')
const webpackConfig = originalConfig // 原来的 module.exports 代码赋值给变量 webpackConfig

module.exports = vuxLoader.merge(webpackConfig, {
  plugins: ['vux-ui']
})

[email protected] 已经停止维护,请尽快迁移到 [email protected] & [email protected] & [email protected],虽然要花点时间,但是完全值得。

2.2 快速开始

[email protected] 推荐webpack+vue-loader方式的开发,如果要使用umd文件,请参照文档。 不建议使用引入script的方式进行开发,因为它会带来一系列的开发、维护、效率、部署问题。

Life is short, use webpack.

vux2 模板


vux2 模板 fork 自 webpack 模板,基本和官方同步。

默认为 webpack2 模板

npm install vue-cli -g # 如果还没安装
vue init airyland/vux2 projectPath

cd projectPath
npm install --registry=https://registry.npm.taobao.org # 或者 cnpm install 或者  yarn
npm run dev #  或者  yarn dev

使用淘宝 npm 镜像


cnpm

你可以直接使用 cnpm 来加速模块下载。

yarn

或者如果你已经用上了 yarn,建议配置淘宝源:

yarn config set registry https://registry.npm.taobao.org
yarn

2.3 手动配置使用

注意的是下面事项并非表示 VUX 使用繁琐,部分只是出于确保有正确的依赖和配置,而部分是出于优化。

请将babel-loader的配置写到.babelrc里而不是使用options,否则可能会出错。

折腾能力强的同学参考一下,下面即airyland/vux2模板主要处理的事项:

  • 引入reset.less,默认样式不包含reset,并且部分用户自己有一套reset样式,因此需要在App.vue进行手动引入
<style lang="less">
@import '~vux/src/styles/reset.less';
</style>
  • 配置vue-loader通过配置vux-loader实现)
// vux-loader
plugins: [{
  name: 'vux-ui'
}]
  • 配置babel-loader以正确编译 VUX 的js源码(通过配置vux-loader实现)
plugins: [{
  name: 'vux-ui'
}]
  • 安装less-loader以正确编译less源码
npm install less less-loader --save-dev
  • 安装yaml-loader以正确进行语言文件读取
npm install yaml-loader --save-dev
  • 添加viewportmeta
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
  • 添加Fastclick移除移动端点击延迟
const FastClick = require('fastclick')
FastClick.attach(document.body)
  • 添加vue-router(如果不需要前端路由,可忽略)
import VueRouter from 'vue-router'
Vue.use(VueRouter)
  • 添加 webpack plugin, 在构建后去除重复css代码(通过配置vux-loader实现)
plugins: [{
  name: 'duplicate-style'
}]
  • 如果你使用webpack-simple模板或者 webpack 配置里缺少 .vue extension 配置,请记得配置:
resolve: {
  extensions: ['.js', '.vue', '.json']

2.4 代码示例

  • .vue文件中调用组件
<template>
  <div>
    <group>
      <cell title="title" value="value"></cell>
    </group>
  </div>
</template>

<script>
import { Group, Cell } from 'vux'

export default {
  components: {
    Group,
    Cell
  }
}
</script>
  • main.js中调用plugin
import { AlertPlugin, ToastPlugin } from 'vux'

Vue.use(AlertPlugin)
Vue.use(ToastPlugin)

// 详细使用请参考对应组件文档

3 定制

3.1 主题颜色配置

配置插件

暂时只支持配合vux-loader使用。

注意的是主题文件不能引入其他less文件,只能为简单变量列表。

请配置vux-loader的less-theme插件,指定用以覆盖的less文件路径:

{
  name: 'less-theme',
  path: 'src/styles/theme.less' // 相对项目根目录路径
}

可配置颜色

源码地址:https://github.com/airyland/vux/blob/v2/src/styles/variable.less

更多配置需求请通过 issue 提出。

demo站点的示例配置

源代码地址:https://github.com/airyland/vux/blob/v2/src/theme.less

内部如何实现的?

vux-loader在每个less文件的编译过程中重写了less-loader的变量参数,使其能直接覆盖原来变量。

4 开发

4.1 路由

4.2 在Nuxt.js中使用

4.3 TypeScript 支持

4.4 Ajax 请求

4.5 点击延迟

4.6 使用微信 jssdk

4.7 添加谷歌统计

4.8 页面切换显示 Loading

4.9 异步加载组件

4.10 区分测试环境和生产环境

4.11 全局公共函数

4.12 autoprefix 配置

4.13 禁用 eslint

5 vux-loader

5.1 vux-loader 是什么?

5.2 安装使用

5.3 插件列表

6 最佳实践

6.1 VUX tabber切换图标及字体颜色

通过path判断当前页面,并切换选中tabbar,如果tabbar不多,使用组件一般方式即可(即在每个子模块引入tabbar并写好默认选中项)

/src/App.vue

<!-- 入口文件 -->
<template>
  <div id="app">
    <!-- 视图层 -->
    <router-view></router-view>
    <div class="footer-box" v-if="$route.path === '/events' || $route.path === '/lending' || $route.path === '/user'">
      <!-- 底部选项卡 -->
      <tabbar class="footer-fixed">
        <tabbar-item link="/lending" :selected="$route.path === '/lending'">
          <img slot="icon" src="./assets/images/foot-tz.png">
          <img slot="icon-active" src="./assets/images/foot-tz-active.png">
          <span slot="label">首页</span>
        </tabbar-item>
        <tabbar-item link="/events" :selected="$route.path === '/events'">
          <img slot="icon" src="./assets/images/foot-xx.png">
          <img slot="icon-active" src="./assets/images/foot-xx-active.png">
          <span slot="label">活动</span>
        </tabbar-item>
        <tabbar-item link="/user" :selected="$route.path === '/user'">
          <img slot="icon" src="./assets/images/foot-wd.png">
          <img slot="icon-active" src="./assets/images/foot-wd-active.png">
          <span slot="label">我的</span>
        </tabbar-item>
      </tabbar>
    </div>
  </div>
</template>

<script>
  // 引入 vux tabbar 组件
  import { Tabbar, TabbarItem, XHeader } from 'vux'
  export default {
    name: 'app',
    components: {
      Tabbar,
      TabbarItem,
      XHeader
    }
  }
</script>

<style src="./assets/style/main.less"></style>
<style lang="less">
  @import '~vux/src/styles/reset.less';
</style>

6.2 x-header、tabbar固定位置

<template>
  <div id="app">
    <router-view></router-view>
    <div class="footer-box" v-if="$route.path === '/events' || $route.path === '/lending' || $route.path === '/user'">
      <tabbar class="footer-fixed">
        <tabbar-item link="/lending" :selected="$route.path === '/lending'">
          <img slot="icon" src="./assets/images/foot-tz.png">
          <img slot="icon-active" src="./assets/images/foot-tz-active.png">
          <span slot="label">首页</span>
        </tabbar-item>
        <tabbar-item link="/events" :selected="$route.path === '/events'">
          <img slot="icon" src="./assets/images/foot-xx.png">
          <img slot="icon-active" src="./assets/images/foot-xx-active.png">
          <span slot="label">活动</span>
        </tabbar-item>
        <tabbar-item link="/user" :selected="$route.path === '/user'">
          <img slot="icon" src="./assets/images/foot-wd.png">
          <img slot="icon-active" src="./assets/images/foot-wd-active.png">
          <span slot="label">我的</span>
        </tabbar-item>
      </tabbar>
    </div>
  </div>
</template>

<script>
  import { Tabbar, TabbarItem, XHeader } from 'vux'
  export default {
    name: 'app',
    components: {
      Tabbar,
      TabbarItem,
      XHeader
    }
  }
</script>

<style src="./assets/style/main.less"></style>
<style lang="less">
  @import '~vux/src/styles/reset.less';
</style>

此处组件fix失效是因为在app.vue引用tabbar导致的,若在子页面分别引用不会影响固定效果

处理方案:

添加class 固定于页面底部

../src/assets/style/main.less

.header-fixed{
  position: fixed;
  top: 0;
  z-index: 500;
  width: 100%;
}
#app .footer-fixed{
  position: fixed;
  bottom: 0;
}
.header-box{
  padding-top: 46px;
}
.footer-box{
  padding-bottom: 53px;
}

6.3 下拉加载更多

先上效果图

  • 创建项目

使用vue-cli 创建一个vue项目

安装vux,可以参考:vux快速入门

  • 配置

官方文档中声明,该组件已经不再维护,也不建议使用,大部分情况下也不需要用到该组件。 建议使用第三方相关组件,相关 issue 将不会处理。不知道作者为啥不维护了,明明需求挺多的

我没有用demo里的 LoadMore 组件,用的是 Scroller里自带的 use-pullup, use-pulldown 下面是我的配置

代码示例:

<template>
  <div>
    <scroller use-pullup :pullup-config="pullupDefaultConfig" @on-pullup-loading="loadMore"
              use-pulldown :pulldown-config="pulldownDefaultConfig" @on-pulldown-loading="refresh"
              lock-x ref="scrollerBottom" height="-48">
      <div>
        <group label-width="4.5em" label-margin-right="2em" label-align="right" v-for="item in list" :key="item.key">
          <panel :list="item.panel" :type="item.type" @on-img-error="onImgError" @click.native="goto(item)"></panel>
          <x-progress :percent="item.progress" :show-cancel="false"></x-progress>
        </group>
      </div>
    </scroller>
  </div>
</template>
<script>
  import axios from 'axios'
  import _ from 'lodash'
  import { Group, Panel, XProgress, Scroller } from 'vux'

  const pulldownDefaultConfig = {
    content: '下拉刷新',
    height: 40,
    autoRefresh: true,
    downContent: '下拉刷新',
    upContent: '释放后刷新',
    loadingContent: '正在刷新...',
    clsPrefix: 'xs-plugin-pulldown-'
  }
  const pullupDefaultConfig = {
    content: '上拉加载更多',
    pullUpHeight: 60,
    height: 40,
    autoRefresh: false,
    downContent: '释放后加载',
    upContent: '上拉加载更多',
    loadingContent: '加载中...',
    clsPrefix: 'xs-plugin-pullup-'
  }

  export default {
    name: 'Lending',
    components: {
      Group,
      Panel,
      XProgress,
      Scroller
    },
    data () {
      return {
        list: [],
        curPage: 1,
        swiper_index: 1,
        pullupDefaultConfig: pullupDefaultConfig,
        pulldownDefaultConfig: pulldownDefaultConfig
      }
    },
    methods: {
      swiper_onIndexChange (index) {
        this.swiper_index = index
      },
      onImgError (item, $event) {
        console.log(item, $event)
      },
      /**
       * 刷新页面
       */
      refresh () {
        console.log('refresh')
      },
      /**
       * 加载更多列表
       */
      loadMore () {
        var self = this
        console.log('加载更多')
        self.$refs.scrollerBottom.donePullup()
        self.curPage++
        self.getList()
      },
      /**
       * 获取列表
       */
      getList () {
        var self = this
        axios.get(process.env.BASE_API + '/financeJson.do', {params: { 'curPage': self.curPage }})
          .then(function (res) {
            _.each(res.data, function (v, k) {
              var item = {
                data: v,
                key: v.id,
                type: '4',
                progress: parseInt(v.progress),
                panel: [
                  {
                    title: v.borrowTitle,
                    desc: '融资额度:' + v.borrowAmount + ' 期限:' + v.deadline,
                    meta: {
                      source: '年利率',
                      date: '7% + ' + (parseInt(v.annualRate) - 7) + '%',
                      other: '完成比例: ' + parseInt(v.progress) + '%'
                    }
                  }
                ]
              }
              self.list.push(item)
            })
          })
          .catch(function (error) {
            console.log(error)
          })
      },
      goto (item) {
        var self = this
        self.$router.push({name: 'financeDetail', params: {data: item}})
      }
    },
    created () {
      var self = this
      self.getList()
    }
  }
</script>

6.4 scroller下拉失败回弹

引用vux中的scroller插件注意事项: 1、scroller标签内部必须紧套一层div标签 2、注意scroller的enabled属性,表示可以下拉刷新

6.5 Vue下路由History模式打包后页面空白

vue的路由在默认的hash模式下,默认打包一般不会有什么问题,不过hash模式由于url会带有一个#,不美观,而且在微信分享, 授权登录等都会有一些坑.所以history模式也会有一些应用场景.新手往往会碰到history模式打包后页面一片空白的情况, 而且没有资源加载错误的报错信息.这个其实仔细研究下会发现,如果项目直接放的跟目录, 那么是没有问题的,如果是子目录, 那么就会一片空白了.这个vue官方有解释,需要加一个base

// base: '/history',
// mode: 'history',

这个base即为项目路径.以上两个都解决了,还是会发现,此时只有首页能访问,通过首页点进去其他路由也是可以的, 但是如果在其他路由刷新就有错误了,这个懂history模式的也应该知道,history模式是h5 api的 history.pushState, 相对于是浏览器模拟了一条历史,而真正服务器上没有这个路径资源,为什么hash模式不存在这个问题呢?hash模式是带#, 这个服务器不会解析,相对于还是返回html而已,index.html会根据vue路由去解析,而history模式则会请求服务器上的此地址, 服务器上没有相关路径就会报错了,vue-router的官方文档有介绍各种配置,比如ngnix的配置

location / {
  try_files $uri $uri/ /index.html;
}

上面这个对于直接项目的根目录是可以的,但是如果项目不是根目录还是会有问题,

location /history {
           root   C:/web/static;
 index  index.html index.htm;
  #error_page 404 /history/index.html;
    if (!-e $request_filename) {
        rewrite ^/(.*) /history/index.html last;
        break;
    }
}

上面这个是项目路径名为history,这样配置后就不会有vue打包后页面空白问题了,history路由也可以自由访问了, 不过要记得上面说的,非根目录的项目需要加上base 的路径

6.6 打包后css引用图片资源找不到

使用vue打包,通过css引用图片资源。

.img {
    height: 500px;
    width: 100%;
    background: url("./assets/img/1.jpg") no-repeat;
    background-size: 100%;
}

热更新开发环境的效果是这样

但打完包出来的页面却报找不到资源的错误。

查了一下原因,css引入图片再打包后,style-loader无法设置自己的publicPath,于是我改变了ExtractTextPlugin的css路径publicPath。

if (options.extract) {
  return ExtractTextPlugin.extract({
    use: loaders,
    // css 引用图片打包问题
    publicPath: '../../../',
    fallback: 'vue-style-loader'
  })
} else {
  return ['vue-style-loader'].concat(loaders)
}

在build一次,没有报错,正常显示!

6.7 打包后js引用图片资源找不到

在vue组件的js部分导入图片要使用require的形式导入,否则webpack不能正常导入,因为其路径不符合其解析规范

原因:

  1. 在引入图片的时候的地址数据的时候需要使用require来讲图片作为模块引入,这样才会被webpack正确的打包的项目文件中,否则会因为webpack根据依赖打包而找不到指定的图片
  2. 如果是在HTML中的img中通过v-bind引入引入图片,src为对应的数据变量的话也是需要使用require来将图片作为模块引入才会被webpack正常打包
  3. 在HTML中直接书写的img地址以及css中引入的图片地址无需使用其他手段正常引用即可被webpack正常打包

6.8 vux框架组件自定义样式

6.8.1 全局方式

方法一 在webpack.base.conf.js文件中配置

{
    name: 'less-theme', 
    path: 'src/styles/theme.less' // 相对项目根目录路径
}

然后在配置的路径写入对应的.less文件,类似下图这样

6.8.2 局部方式

方法二 使用/deep/或>>>

在引用的组件包一个div,例如类名为sample

要改变里面组件样式可以用在<style>标签中用.sample /deep/ xxxxx组件样式类来选择

注意:/deep/在less和sass中不支持,本人在使用>>>测试的时候没有生效

6.9 vux-cell title 插槽使用

<group>
    <cell title="消息中心">
        <span slot="default"><span style="vertical-align:middle;"></span> <badge text="99"></badge></span>
    </cell>
    <cell>
        <span slot="title" class="edit-left">手机号码</span>
        <span slot="default">{{phone}}</span>
    </cell>
    <cell title="第三方登录授权" >
        <span slot="default"><img class="icon" slot="icon" src="../../assets/wx_icon.png"></span>
    </cell>
    <cell title="登录密码">
        <span slot="default"></span>
    </cell>
</group>
  • 注意:样式被修改,可能是公共样式或其他模块样式在打包后被修改

vux cell title插槽可添加样式并使得超长文字隐藏。 可参考/yumaomoney_WeChat/src/components/user/message/Message.vue .cell-overflow

6.10 报错处理:Failed to load resource: net::ERR_FILE_NOT_FOUND

Failed to load resource: net::ERR_FILE_NOT_FOUND或者vue dist文件下的index.html没显示

vue-cli  npm run dev 可以看到,但是通过dist文件下的index.html直接打开没显示,没看到  

知乎上:webpack.prod.conf.js 中output添加参数publicPath:'./'

具体:

在webpack.base.conf.js里

publicPath: process.env.NODE_ENV === 'production'
     ? './' +config.build.assetsPublicPath
     : './' + config.dev.assetsPublicPath

vue的图片路径,和背景图片路径打包后错误解决

  • 找到 config->index.js里面,如下修改

  • 找到 build->utils.js,在里面加入一句publicPath:’../../’

配置修改完成,接下来,使用有两种方式,这里一般和文件结构有关,下面是我的文件结构下的使用

1、图片资源放在 assets->img文件夹下面

img标签引入图片

<img src="../assets/img/loginback.png" class="test-img" />

css使用图片

background: url('../assets/img/loginback.png') no-repeat top left ;

2、图片资源放在static->img文件夹下面

img标签引入图片

<img src="../../static/img/loginback.png" class="test-img" /><br><img src="static/img/loginback.png" class="test-img" />

css使用图片

background: url('../../static/img/loginback.png') no-repeat top left ;

6.11 警告处理:warning:component lists rendered with v-for should have explicit keys

命令行warning(Emitted value instead of an instance of Error)。 component lists rendered with v-for should have explicit keys。 See https://vuejs.org/guide/list.html#key for more info.

截图如下:

这里只是推荐使用key. 原本的代码如下:

<el-tag
    v-for = "feiLei of ruleForm.fenLeis"          
    :closable = "true"
    type = "primary"
    @close = "handleCloseFenLei(feiLei)">{{feiLei.name}}
</el-tag>

运行时显示warning,添加:key即可,如下:

<el-tag
    v-for = "feiLei of ruleForm.fenLeis"
    :key="feiLei1"        
    :closable = "true"
    type = "primary"
    @close = "handleCloseFenLei(feiLei)">{{feiLei.name}}
</el-tag>

这样就不会报错啦,具体看文档,key不是必须的,仅仅是warning

6.12 报错处理:exports is not defined

在引入插件后,控制台报错Uncaught ReferenceError: exports is not defined

处理方法:

  • 引入插件需重新编译,重新npm run dev
  • webpack 2后不允许混合使用import和module.exports
    • 统一修改为export default XXX
    • 找到.babelrcf删除transform-runtime
  • 去掉 { "modules": false }其中{ "modules": false }阻止了babel进行模块转换,具体见modules配置的说明
    • 将modules改为默认设置即可
    • 删除该配置

6.13 报错处理:Default export is not declared in imported module

升级 webstorm 到 2016.1 即可解决,以前的版本有这个规则但是没选项关闭

可参考yumaomoney_WeChat/src/components/container/Container.vue,export default为必要内容。

7 同类型技术比较

参考资料

results matching ""

    No results matching ""