现状及问题

项目组前端比较少,为了快速开发,使用的是uniapp来发布多端项目,uniapp目前无法整合web,所以web端使用的是付费的iview-pro组件库来实现。

因为项目需求变动比较快,为了避免一套逻辑实现多次,使用的是嵌入的方式来复用web和uniapp相同的功能。

这会有如下几个问题:

  • web端的SPA在初次加载时很慢,白页时间很长,用户体验不佳
  • 通过自适应的方式适配手机和PC端,前端开发需要考虑两端,工作量较大,部分情况下,调整的难度可能并不比写两套页面简单。最终适配效果也不一定理想
  • 嵌入页面与父页面的交互复杂,需要使用各种通信机制来处理通信,对前端要求较高,且不易于测试。流程上需要考虑多端,增加思考负担
  • webview缓存问题,加载的页面可能会有缓存,导致不手动刷新无法清除缓存(部分情况即使刷新也无法清除缓存)

同时还有项目混乱的问题:

  • uniapp和web端项目结构不统一
  • 功能相同的组件有多套
  • uniapp无法自动发布,需要基于HBuilderX打包后才能发布

目标

鉴于上面的问题,考虑在不增加前端开发负担的情况下,解决这些问题,期望:

  • 一套代码在web和uniapp中公用,避免相同的功能在web和uniapp重复实现
  • 只需要考虑单端流程,不需要考虑多端流程,降低前端思考负担(目前使用的嵌入方式是需要考虑多端流程和问题的,无形中增加了前端的思考负担)
  • 统一web和uniapp的项目结构和语法,目前web使用npm-cli来管理,uniapp使用的hbuilder来创建的
  • 统一部署方式(目前uniapp需要前端打包)
  • 确定前端代码规范(目前前端代码较乱,发送请求的方式就有好几种!)
  • 提供统一的用户体验(uniapp提供类原生体验,web提供网页体验),尽量避免嵌入导致的白页问题

思路

首先,uni-app在发布到H5时支持所有vue的语法发布到App和小程序时,由于平台限制,无法实现全部vue语法。


具体差异详见:https://uniapp.dcloud.io/use

上文列出的差异数量有限,也就是说,只要避免了这些差异,就能保证uniapp和web代码的基本语法一致性,注意这里是基本语法

其次,uniapp是支持vue-cli以及npm包依赖的。也就是说,uniapp和web可以共用仓库,这就解决了共用代码的管理问题。类似Java,将共用代码打包发布到仓库,uniapp和web分别引用的方式来使用。


具体见:

https://uniapp.dcloud.io/quickstart?id=_2-%e9%80%9a%e8%bf%87vue-cli%e5%91%bd%e4%bb%a4%e8%a1%8c

https://uniapp.dcloud.io/frame?id=npm%e6%94%af%e6%8c%81

不过考虑到前期代码改动会比较频繁,以这种方式处理还是比较麻烦(需要修改、打包、发布、改版本号在install)。所以直接基于git的subtree来实现代码层面的共享(修改、提交、拉取),待代码稳定后,再进行库的管理。

最后,就是组件库问题。web目前使用的是iViewAdminUI,uniapp使用的是原生UI。

uniapp原生UI见:https://uniapp.dcloud.io/component/README

两者差异:https://ask.dcloud.net.cn/article/35489

两者的差异见上文,主要体现在:


  • 兼容性:iView组件是针对浏览器的,里面有大量dom和window对象操作。但小程序和App是没有dom这些api的,所以无法跨端使用。即无法在uniapp里使用iView
  • 性能:vue组件性能好于小程序自定义组件,即uniapp提供的组件性能要优于第三方组件性能

所以最主要的问题,就是如何在web端和uniapp端提供一套语法一致的UI组件库。再结合上面两个功能,即可提供一套多端语法完全一致的开发规范。

实际上,要提供一套语法一致的UI组件库并不难,只是需要花费一点时间,在原UI库上再封装一层即可。详见下节。


方案

计算机科学中遇到的所有问题都可通过增加一层抽象来解决。

All problems in computer science can be solved by another level of indirection。

by David Wheeler

简单来说,就是通过抽象一层来解决

考虑到vue的使用人群更广,也更偏底层,所以在uniapp上抽象来适配vue

这样能解决两个问题:

  • 适配问题,由框架来处理,而不需要开发来考虑自适应问题。相当于将问题从运行期提前到了编译期。
  • 不需要为了兼顾多端,而选择一个折中的方案。比如:为了手机端的展示,web端不能使用table。通过适配,可以在手机端将table适配成list。

假设,web端有如下代码:

{{item.name}}

这段代码在web端可以直接运行,但是Select在手机端其实并不友好,比较友好的方式应该是Picker,所以,我们就可以在uniapp里编写一个组件select.vue(这部分代码通过组件库的方式来编写,项目引用即可。)

当前选择:{{array[index]}}

将其注册到项目中,组件名叫Select,这样上面的Select就会被解释为Picker来处理了。

实施步骤

上面的方案,具体可以分为如下几步实施:

  • 统一项目结构:迁移HBuilderX创建的项目到vue-cli。统一前端项目的结构和打包发布方式。
  • 统一请求调用:封装统一的AJAX请求API供两端使用。初步构建common库,初步统一代码规范。
  • 制定语法规范:只使用uniapp和vue均支持的语法,同时对于相同功能的代码,语法要保持一致。比如路由跳转。进一步统一规范。
  • 二次封装uniapp组件:适配iView组件。封装统一的组件库,完成多端统一,这是一个持续过程。

迁移HBuilderX创建的项目到vue-cli

  • 基于vue-cli创建uniapp

# 创建uni-app

vue create -p dcloudio/uni-preset-vue my-project

## 如果执行失败,可以访问如下git,下载zip包,解压

https://github.com/dcloudio/uni-preset-vue

## 然后执行

vue create -p #{解压目录} my-project

# 运行、发布uni-app

npm run dev:%PLATFORM%

npm run build:%PLATFORM%

%PLATFORM% 可取值如下:




uniapp progress能加阴影效果吗_前端vue适配不同的分辨率


  • 将 HBuilderX 工程内的文件(除 unpackage、node_modules 目录)拷贝至 vue-cli 工程内 src 目录
  • 在 vue-cli 工程内重新安装 npm 依赖(如果之前使用了 npm 依赖的话)

npm i node-sass -D

npm i sass-loader -D

基于git的subtree的代码公用方法

注意,虽然subtree可以任意修改提交,不过还是尽量在一处修改,比如在admin-vue项目中编写,否则合并时冲突解决比较麻烦

使用步骤:

  • 创建一个项目,用于存放需要公用的代码,正常创建即可(创建完后,基本就可以不用管了)
  • 在需要使用同步代码的项目中执行如下命令(只需要执行一次):

# module是取的别名

git remote add -f module ${上面的项目git地址}

# 将这个项目拉取到 src/module目录下

git subtree add --prefix=src/module module master --squash

  • 在项目中修改模块代码后,正常push。然后使用如下命令同步(每次修改共享代码后执行):

git subtree push --prefix=src/module module master

  • 需要同步的项目,执行如下命令(如果需要同步共享代码,则执行):

git subtree pull --prefix=src/module module master

基于npm仓库的代码公用方法

下载npm包

npm config set registry http://***/repository/npm-public/

设置后,正常使用npm即可

上传npm包

通过

npm adduser

添加用户!

项目中需要有package.json,注意下面的配置

{
 "name": "vue-tmp",
 "version": "1.0.0",
 "private": false,
 "publishConfig" : {
 "registry" : "http://***/repository/npm-releases/"
 },
...
}

private为false才能上传,publishConfig配置的是上传的仓库路径

使用如下命令登录registry!

npm adduser --registry=http://***/repository/npm-releases/

在项目根目录下执行:

npm publish

即可上传成功

注意:scripts中不能有publish,否则会触发二次publish!正式releases仓库是关闭reploy的!

"scripts": {
 "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
 "start": "npm run dev",
 "unit": "jest --config test/unit/jest.conf.js --coverage",
 "e2e": "node test/e2e/runner.js",
 "test": "npm run unit && npm run e2e",
 "lint": "eslint --ext .js,.vue examples src test/unit test/e2e/specs",
 "build": "node build/build.js"
 },