weex腳手架

此篇文章不要注意排版css

經上級領導的要求,咱們公司開始步入weex的隊列,雖然如今已經處於開始階段,可是是一個好的開始,一個艱苦的開始。html

廢話很少說,咱們先聊一聊剛開始的整個過程vue

1、關於運行weex項目

npm要求5.+,所以安裝了node8.7.0,自帶安裝了 npm 5.4.2
爲了方便切換node版本,mac上咱們能夠安裝n來管理
sudo npm n -g
n 8.7.0便已切換node

爲了 npm install 的速度快一點,設置淘寶鏡像
npm config set registry https://registry.npm.taobao.orgwebpack

2、開始weex

1.安裝weex: sudo npm install -g weex-toolkit
2初始化工程:weex init projectName
3.運行demo
weex src/index.vue
而後便可以使用playground app二維碼掃描來查看效果了git

個人weex版本:web

clipboard.png

3、開始本身的腳手架

首先weex號稱能夠一套代碼跑三端,那麼咱們暫且區分兩端,原生和H5.
網上巴拉巴拉查詢一通,可使用vue-router寫單頁面,可是聽說在原生APP上切換頁面的時候很卡,由於是dom級別的切換,因而,查到建議使用navigator來跳轉vue-router

而後,而後咱們就想辦法,本身封裝一個router,讓咱代碼既兼容vue-router,也兼容原生。
如下是個人項目目錄:npm

clipboard.png

原生端route
weex-routes.js文件json

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的

clipboard.png

還須要注意的地方:因爲咱們也是剛開始接觸weex,但願這這只是一個參考案例,畢竟咱們也不是高手。

相關文章
相關標籤/搜索