背景
最近一直在试点用
uni-app
去做一些小需求,分享一下在项目中遇到的问题以及一些经验。ps:本项目整体为vue-cli
创建,项目目录结构会与hbuilderx
有差异。本项目开源于Github
项目目标
做一个用户线上预约,线下到店体验的简单预约商城小程序,常见于
美业
婚庆/摄影
教育/培训
亲子
等线下场景。小程序端使用uni-app
,后端API使用nestjs
+mongo
,服务端部署使用docker
。
系列连载
选择uni-app还是trao?
项目的选型有很多文章,很多分析。抛开项目大小谈性能,纯属于瞎扯。选择主要还是根据团队来,团队里学习vue
的是主力,那项目选择uni-app
会好一点。反之主力是react
那选择trao
会好点。没有哪一个框架是没有坑的,要看你是否有解决的能力。
我这里之前的项目都是wepy
的,类似vue
,所有选择了uni-app
去开发,会有更多相似之处。同时我还选择了TypeScript
来写uni-app
。其实vue2.x
对ts
的支持并不美丽,但终究好于js
。给自己挖坑。
优雅的使用@vant/weapp
每一个项目的搭建的时候,我都希望能工程化它。尽可能的减少重复的劳动,同时也希望能更好的优化它。
选好项目框架就要去选择对应的ui
框架了。@vant/weapp
应该会比较适合,毕竟项目是会参考有赞界面的。
前提
得先有一个项目,vue create -p dcloudio/uni-preset-vue vant2uni
@vant/weapp
的安装很简单,npm i @vant/weapp -S --production
,但是这样并不能用于uni
项目。
@vant/weapp
是微信小程序原生组件的写法,在uni
内微信小程序的原生组件需要放到src/wxcomponents
内。
原始的话大概这么用
-
有些人会去
node_modules
目录下找到@vant/weapp
然后拷贝到src/wxcomponents
内,大概是大功告成了。优点:能下载最新版本的
@vant/weapp
缺点:然后微信开发者调试的时候,提示很多文件无法上传,主要是
*.d.ts
文件,无碍的(也可以拷贝lib
目录下的,无
*.d.ts
文件)。只能手工拷贝同步@vant/weapp
版本。只能整体打包进项目。 -
去
dcloud
插件市场下载。(当初有小伙伴建议使用这种方案)优点:没发现
缺点:版本差异太大了,完全无法同步
@vant/weapp
版本。只能整体打包进项目。ps:版本无法同步,这是交流群里遇到最多的问题,你可能都不知道你下载的是哪个版本。
我是怎么用的?
我们目标很明确:能把任意版本的
@vant/weapp
文件放到项目的src/wxcomponents
文件夹内,去除不必要的*.d.ts
文件。这不就是选择性的拷贝嘛?分析:
uni
项目是vue-cli
创建的,那不就是自带webpack
了么。拷贝插件就有现成的copy-webpack-plugin
。如何使用
wxcomponents
内的文件参考官方文档小程序组件支持
新建vue.config.js
文件与src
目录同级。这里是vue-cli
的知识。
const CopyWebpackPlugin = require('copy-webpack-plugin') // 拷贝插件
const path = require('path')
module.exports = {
chainWebpack: config => {
// 我这里直接往dist目录拷贝,不拷贝到src了。看过uni源码就知道src/wxcomponents下的文件也是通过拷贝到dist/wxcomponents
config.plugin('CopyWebpackPlugin')
.use(new CopyWebpackPlugin([{
from: path.join(__dirname, `/node_modules/@vant/weapp/dist/`),
to: path.join(__dirname, `/dist/${process.env.NODE_ENV === 'development' ? 'dev' : 'build'}/mp-weixin/wxcomponents/@vant/weapp/dist/`)
}], {
ignore: ['*.d.ts'] // 忽略文件
}))
}
}
用法优点不同:在uni项目内,写法就是vue写法了。要抛开@vant/weapp示列的微信原生小程序写法。
// 原生小程序写法示列
<van-button bind:click="click"></van-button>
// uni内 vue语法示列
<van-button @tap="click"></van-button>
大功告成了?想要哪个版本的@vant/weapp
直接npm i @vant/weapp@x.x.x
就可以了。在项目开发模式npm run dev:mp-weixin
的时候会自动把你安的 @vant/weapp
拷贝到wxcomponents
下去。copy-webpack-plugin
可以监视拷贝文件的变化,所以就算你更新@vant/weapp
版本后也会自动拷贝过去。
以上:已经能做到自动同步@vant/weapp
版本了,但是依旧会把整体打包进项目。很多不需要的组件都被拷贝进去了。
进阶使用,删除不需要拷贝的包
使用任何微信自定义组件,都需要在
pages.json
的里面引入。微信自定义组件还可能引用其他组件,同样需要在组件目录下的
*.json
内usingComponents
引入。
有了以上分析,就是读取json
文件,获取usingComponents
字段,最终获取在项目中使用上的@vant/weapp
的组件名称。
新建文件vant2uni.js
放在plugins
目录下,plugins
与src
同级。
// 让Vant用于 uni项目
const chalk = require('chalk')
const path = require('path')
const fs = require('fs')
const get = require('lodash.get')
const stripJsonComments = require('strip-json-comments')
// 默认拷贝目录 有讲究的话可以再具体细分 可能意义不大了
// 这是@vant/weapp内组件公用的地方
let defaultCopy = ['common', 'wxs', 'mixins']
// 独立出来的方法 组件依赖寻找
const deepFindUsingComponents = (jsonPath) =>{
let jsonFilePath
if (jsonPath.indexOf('/wxcomponents/') !== -1) {
jsonFilePath = path.resolve(process.env.UNI_INPUT_DIR, `${jsonPath.replace('/wxcomponents/', '../node_modules/')}.json`)
} else {
jsonFilePath = path.resolve(process.env.UNI_INPUT_DIR, `${jsonPath.replace('../', '../node_modules/@vant/weapp/dist/')}.json`)
}
let pagesJson = {}
try {
pagesJson = JSON.parse(stripJsonComments(fs.readFileSync(jsonFilePath, 'utf8')))
} catch (error) {}
let usingComponents = pagesJson.usingComponents || {}
if (pagesJson.usingComponents) {
const _deepUsingComponentsPathArr = Object.values(pagesJson.usingComponents) || []
for (let index = 0; index < _deepUsingComponentsPathArr.length; index++) {
const obj = _deepUsingComponentsPathArr[index]
usingComponents = {
...usingComponents,
...deepFindUsingComponents(obj)
}
}
}
return usingComponents
}
module.exports = Vant2Uni = () => {
const jsonFilePath = path.resolve(process.env.UNI_INPUT_DIR, 'pages.json')
const pagesJson = JSON.parse(stripJsonComments(fs.readFileSync(jsonFilePath, 'utf8')))
let allUsingComponents = {}
/**
* 获取各个page中的 usingComponents
*/
for (let index = 0; index < pagesJson.pages.length; index++) {
allUsingComponents = {
...allUsingComponents,
...get(pagesJson.pages[index], 'style.usingComponents')
}
}
/**
* 获取globalStyle中的usingComponents
*/
allUsingComponents = {
...allUsingComponents,
...get(pagesJson, 'globalStyle.usingComponents', {})
}
/**
* 组件依赖关系的usingComponents
*/
const allUsingComponentsPathArr = Object.values(allUsingComponents)
for (let index = 0; index < allUsingComponentsPathArr.length; index++) {
allUsingComponents = {
...deepFindUsingComponents(allUsingComponentsPathArr[index]),
...allUsingComponents
}
}
defaultCopy = defaultCopy.concat(Object.keys(allUsingComponents))
process.stdout.write(chalk.green(`\n*** 拷贝Vant以下组件 ***: ${defaultCopy}\n`))
return Array.from(defaultCopy, item => {
// 目录名称没有 van- 要删除
return item.replace('van-', '')
})
}
修改vue.config.js
文件引入vant2uni.js
const CopyWebpackPlugin = require('copy-webpack-plugin')
const path = require('path')
const Vant2Uni = require('./plugins/vant2uni')
const getVantPaths = () => {
// 默认全拷贝
let vantPaths = [{
from: path.join(__dirname, `/node_modules/@vant/weapp/dist/`),
to: path.join(__dirname, `/dist/dev/mp-weixin/wxcomponents/@vant/weapp/dist/`)
}]
if (process.env.NODE_ENV !== 'development') {
// Vant2Uni 获取具体需要拷贝的文件夹名
vantPaths = Array.from(Vant2Uni(), item => {
return {
from: path.join(__dirname, `/node_modules/@vant/weapp/dist/${item}`),
to: path.join(__dirname, `/dist/build/mp-weixin/wxcomponents/@vant/weapp/dist/${item}`)
}
})
}
return vantPaths
}
module.exports = {
chainWebpack: config => {
config.plugin('CopyWebpackPlugin')
.use(new CopyWebpackPlugin(getVantPaths(), { copyUnmodified: true, ignore: ['*.d.ts'] }))
}
}
以上'common', 'wxs', 'mixins'
除了这三个目录整体打包其他的,都能做到按需打包进项目了。
写在最后
如果仅仅开发微信小程序项目可以使用@vant/weapp
,如果要开发多平台的话不建议在uni
项目内使用@vant/weapp
了。@vant/weapp
并不能适用于其他的小程序。我最终目标是向往多平台开发的。
折腾这么多只是为了能意识到项目需要工程化,提高项目的可维护性,减少不必要人为操作带来的bug
。很多小伙伴在vant
的群里问,我的mask
遮罩怎么不生效,这个怎么跟官方example
内不一样,我的是方的它是圆的。这种问题...多半是自身的问题。都不知道用的是哪个年代的版本了然后对照官方最新的example
在找差异,浪费的是大家的时间,找到最后发现自己版本过旧了。