此篇文章不要注意排版
经上级领导的要求,我们公司开始步入weex的队列,虽然现在已经处于开始阶段,但是是一个好的开始,一个艰苦的开始。
废话不多说,我们先聊一聊刚开始的整个过程
一、关于运行weex项目
npm要求5.+,因此安装了node8.7.0,自带安装了 npm 5.4.2
为了方便切换node版本,mac上我们可以安装n来管理
sudo npm n -g
n 8.7.0便已切换
为了 npm install 的速度快一点,设置淘宝镜像
npm config set registry https://registry.npm.taobao.org
二、开始weex
1.安装weex: sudo npm install -g weex-toolkit
2初始化工程:weex init projectName
3.运行demo
weex 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: '<div id="root"><router-view></router-view></div>',
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.js
import '../../router.js';
this.$router.push({path: '/material', query: this.form});
// 当跳转到material.vue中我们则可以直接获取url中的参数了,此法兼容原生和H5
import '../../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-loader
const 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,希望这这只是一个参考案例,毕竟我们也不是高手。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。