此篇文章不要注意排版
经上级领导的要求,我们公司开始步入weex的队列,虽然现在已经处于开始阶段,但是是一个好的开始,一个艰苦的开始。
废话不多说,我们先聊一聊刚开始的整个过程
一、关于运行weex项目
npm要求5.+,因此安装了node8.7.0,自带安装了 npm 5.4.2
为了方便切换node版本,mac上我们可以安装n来管理sudo npm n -gn 8.7.0便已切换为了 npm install 的速度快一点,设置淘宝镜像
npm config set registry二、开始weex
1.安装weex: sudo npm install -g weex-toolkit
2初始化工程:weex init projectName3.运行demoweex src/index.vue然后即可以使用playground app二维码扫描来查看效果了我的weex版本:
三、开始自己的脚手架
首先weex号称可以一套代码跑三端,那么我们暂且区分两端,原生和H5.
网上巴拉巴拉查询一通,可以使用vue-router写单页面,但是据说在原生APP上切换页面的时候很卡,因为是dom级别的切换,于是,查到建议使用navigator来跳转然后,然后我们就想办法,自己封装一个router,让咱代码既兼容vue-router,也兼容原生。
以下是我的项目目录:
原生端route
weex-routes.js文件const basePath = 'http://192.168.21.75:8088/dist/views';const routeList = [ {path: '/bankList', component: basePath + '/bankList.weex.js'}, {path: '/bank', component: basePath + '/bank.weex.js'}, {path: '/home', component: basePath + '/home/home.weex.js'}, {path: '/material', component: basePath + '/home/material.weex.js'}, {path: '/user/register', component: basePath + '/user/register/index.weex.js'}, {path: '/user/modifyPassword', component: basePath + '/user/modifyPassword.index.weex.js'},];export default routeList;
web端route配置
web-routes.js文件import bankList from 'views/bankList.vue';import bank from 'views/bank.vue';import home from 'views/home/home.vue';import material from 'views/home/material.vue';import register from 'views/user/register/index.vue';import modifyPassword from 'views/user/modifyPassword/index.vue';const routeList = [ {path: '/bankList', component: bankList}, {path: '/bank', component: bank}, {path: '/home/home', component: home}, {path: '/home/material', component: material}, {path: '/user/register', component: register}, {path: '/user/modifyPassword', component: modifyPassword},];export default routeList;
web端H5由于我们做成一个单页面,所以还需要一个入口文件
app.js文件import VueRouter from 'vue-router';import routeList from './web-routes.js';Vue.use(VueRouter);const router = new VueRouter({ routes: routeList, mode: 'history'});new Vue({ template: '', router}).$mount('#root');
接下来就是我们来封装一下router了,让我们的代码兼容APP和H5端,
router.js文件import routeList from './weex-routes';const navigator = weex.requireModule('navigator');/*** 从weex路由表中获取路由* @params route String|Object*/function getWeexRoute (route) { const item = routeList.find(item => { if (item.path === route.path || route === route.path) { return item; } }); if (!item) { throw new Error(`routes路由表中不存在该路径${route.path}`); } return item;};const routerConfig = { install () { // H5不需要重置router属性,直接返回 if (weex.config.env.rem) { return; } const url = weex.config.bundleUrl; const query = getQueryData(url); Object.defineProperty(Vue.prototype, "$router", { value: { push (route) { const currentRoute = getWeexRoute(route); let query = ''; if (route.query) { query = createQuery(route.query); } navigator.push({ url: currentRoute.component + query, animated: 'true' }); }, back () { if (navigator) { navigator.pop(); } } }, configurable: false }); Object.defineProperty(Vue.prototype, '$route', { configurable: false, value: { query: query, fullPath: '', name: '', params: {}, path: '', hash: '', } }); }}Vue.use(routerConfig);// object 转 URL 参数function createQuery (obj) { let url = '?'; for (let key in obj) { if (obj[key] !== null) { url += (key + '=' + encodeURIComponent(obj[key]) + '&'); } } return url.substring(0, url.lastIndexOf('&'));};// 'xxx.js?name=aa' 转 {name: 'aa'}function getQueryData (url) { url = url.substring(url.indexOf('.js?') + 3); var result = {}; if (url.indexOf("?") != -1) { var str = url.substr(1); var strs = str.split("&"); for (var i = 0; i < strs.length; i++) { result[strs[i].split("=")[0]] = decodeURIComponent(strs[i].split("=")[1]); } } return result; };
ok基础设施已大功告成,我们需要在我们的业务代码中使用router了
// 首先需要引入我们的router.jsimport '../../router.js';this.$router.push({path: '/material', query: this.form});// 当跳转到material.vue中我们则可以直接获取url中的参数了,此法兼容原生和H5import '../../router.js';this.query = this.$route.query;
基础的配置我们已经操作完毕,接下来要配置webpack了
我们需要一个build xx.wexx.js的webpack配置和一个web的单页的webpack配置webpack.web.js配置
const ip = require('ip').address();const path = require('path');const chalk = require('chalk');const webpack = require('webpack');const ExtractTextPlugin = require('extract-text-webpack-plugin');console.log('server is running! Please open ' + chalk.green('http://' + ip + ':8080/'));const HtmlWebpackPlugin = require('html-webpack-plugin');const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');const isProd = process.env.NODE_ENV === 'production';module.exports = function() { const config = { entry: { app: './src/app.js' }, output: { path: path.join(__dirname, './dist'), filename: '[name].[hash:7].web.js', }, resolve: { extensions: ['*', '.vue', '.js'], alias: { 'src': path.join(__dirname, './src'), 'views': path.join(__dirname, './src/views'), 'services': path.join(__dirname, './src/services'), 'utils': path.join(__dirname, './src/utils'), 'constants': path.join(__dirname, './src/constants'), 'assets': path.join(__dirname, './src/assets'), } }, devtool: 'source-map', module: { rules: [ { test: /\.vue(\?[^?]+)?$/, loader: 'vue-loader', }, { test: /\.html$/, loader: 'raw-loader', }, { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ } ] }, plugins: [ new webpack.BannerPlugin({ banner: '// { "framework": ' + ('.vue' === '.vue' ? '"Vue"' : '"Weex"') + '} \n', raw: true, exclude: 'Vue' }), new ScriptExtHtmlWebpackPlugin({ defaultAttribute: 'defer' }) ] }; if (!isProd) { config.plugins.push( new HtmlWebpackPlugin({ template: 'web/index.dev.html', title: 'Hello Weex', isDevServer: true, chunksSortMode: 'dependency', inject: 'head' }) ); config.devServer = { compress: true, host: '0.0.0.0', port: '8080', historyApiFallback: true, public: ip + ':8080', watchOptions: { aggregateTimeout: 300, poll: 1000 } }; } else { // 抽取vue文件css config.module.rules[0].options = { loaders: { css: ExtractTextPlugin.extract({ use: ['css-loader'], fallback: 'vue-style-loader' }) } }; config.plugins.push( new ExtractTextPlugin('[name].[hash:7].css'), new HtmlWebpackPlugin({ template: 'web/index.html', inject: true, }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ) } return config;}
原生端的webpack.config.js配置:
const pathTo = require('path');const fs = require('fs-extra');const webpack = require('webpack');const entry = {};const weexEntry = {};const vueWebTemp = 'temp';const hasPluginInstalled = fs.existsSync('./web/plugin.js');var isWin = /^win/.test(process.platform);function getEntryFileContent(entryPath, vueFilePath) { let relativePath = pathTo.relative(pathTo.join(entryPath, '../'), vueFilePath); let contents = ''; if (hasPluginInstalled) { const plugindir = pathTo.resolve('./web/plugin.js'); contents = 'require(\'' + plugindir + '\') \n'; } if (isWin) { relativePath = relativePath.replace(/\\/g,'\\\\'); } contents += 'var App = require(\'' + relativePath + '\')\n'; contents += 'App.el = \'#root\'\n'; contents += 'new Vue(App)\n'; return contents;}var fileType = '';function walk(dir) { dir = dir || '.'; const directory = pathTo.join(__dirname, 'src', dir); fs.readdirSync(directory) .forEach((file) => { const fullpath = pathTo.join(directory, file); const stat = fs.statSync(fullpath); const extname = pathTo.extname(fullpath); if (stat.isFile() && extname === '.vue' || extname === '.we') { if (!fileType) { fileType = extname; } if (fileType && extname !== fileType) { console.log('Error: This is not a good practice when you use ".we" and ".vue" togither!'); } const name = pathTo.join(dir, pathTo.basename(file, extname)); if (extname === '.vue') { const entryFile = pathTo.join(vueWebTemp, dir, pathTo.basename(file, extname) + '.js'); fs.outputFileSync(pathTo.join(entryFile), getEntryFileContent(entryFile, fullpath)); entry[name] = pathTo.join(__dirname, entryFile) + '?entry=true'; } if (fullpath.includes('/views')) { weexEntry[name] = fullpath + '?entry=true'; } } else if (stat.isDirectory() && file !== 'build' && file !== 'include') { const subdir = pathTo.join(dir, file); walk(subdir); } });}walk();// web need vue-loaderconst plugins = [ new webpack.optimize.UglifyJsPlugin({minimize: true}), new webpack.BannerPlugin({ banner: '// { "framework": ' + (fileType === '.vue' ? '"Vue"' : '"Weex"') + '} \n', raw: true, exclude: 'Vue' })];const weexConfig = { entry: weexEntry, output: { path: pathTo.join(__dirname, 'dist'), filename: '[name].weex.js', }, module: { rules: [ { test: /\.js$/, use: [{ loader: 'babel-loader', }], exclude: /node_modules(?!\/.*(weex).*)/ }, { test: /\.vue(\?[^?]+)?$/, use: [{ loader: 'weex-loader' }] }, { test: /\.we(\?[^?]+)?$/, use: [{ loader: 'weex-loader' }] } ] }, plugins: plugins,};module.exports = weexConfig;
package.json配置:
"build": "rm -rf dist && cross-env NODE_ENV=production webpack --config webpack.web.js && webpack --config webpack.config.js", "web1": "webpack --config webpack.web.js --watch", "web2": "webpack-dev-server --config webpack.web.js --progress --watch --open", "web": "rm -rf dist&npm run web1&npm run web2"
打包执行 npm run build,就会把weex和H5的文件都给生产到dist目录中了
.weex文件是原生的,.css .web index.html是H5的
还需要注意的地方:
由于我们也是刚开始接触weex,希望这这只是一个参考案例,毕竟我们也不是高手。