Weex小册—从0搭建一个Weex项目
新建项目工程
mkdir weex-starter cd weex-starter npm init -y
然后在根目录下新建index.html文件。
目录结构如下:
. ├── index.html └── package.json
安装依赖
webpack & webpack-dev-server
由于weex-loader暂不支持webpack4,所以,webpack只能安装3.x版本,webpack-dev-server对应安装2.x版本npm i [email protected] [email protected] -D
babel相关
npm i babel-loader babel-core babel-preset-env -D // 新建.babelrc文件 { "presets": ["env"] }
vue相关
npm i vue -S // vue-loader配置项 npm i vue-loader autoprefixer postcss-plugin-weex postcss-plugin-px2rem weex-vue-precompiler -D
weex-loader
weex-loader的作用是把.vue文件转化为native端使用的.weex.jsnpm i weex-loader -D
weex-vue-render
weex-vue-render是 Vue DSL 的 Web 渲染器, 它在 Web 上实现了 Weex 的内置组件和内置模块npm i weex-vue-render -S
此时目录结构如下(忽略node_modules):
. ├── index.html ├── package-lock.json └── package.json
src目录结构
mkdir -p src/entry src/page
entry文件夹是webpack打包的入口,page文件夹是各个.vue页面。
此时目录结构如下(忽略node_modules):
. ├── index.html ├── package-lock.json ├── package.json ├── src │ ├── entry │ └── page └── webpack.config.js
编写代码
weex最常见的使用场景就是编写某一个页面而不是整个APP,即开发一个一个的页面,然后把这个页面放到native端呈现。所以我们的weex-starter工程也是以页面为单位组织的:src/entry文件夹下,一个入口文件会编译出一个页面。
开发页面是在web端进行的,所以我们要依赖vue和weex-vue-render,但是.weex.js
是不需要这两个库的,因为 Weex 本身集成了v2版本的 Vue,而weex-vue-render是 Vue DSL 的 Web 渲染器
。开发以及打包编译时,会同时生成.web.js和.weex.js,所以我们把vue和weex-vue-render以<script>标签的方式直接引入到html中。
<!doctype html> <html> <head> ... <script src="./node_modules/vue/dist/vue.runtime.min.js"></script> <script src="./node_modules/weex-vue-render/dist/index.js"></script> </head> <body> <div id="root"></div> </body> </html>
// src/entry/index.js // import Vue from 'vue/dist/vue.esm'; // import weex from 'weex-vue-render'; import Index from '../page/index.vue'; // weex.init(Vue); Index.el = '#root'; new Vue(Index);
src/page/index.vue就是一个正常的.vue文件,内容自己编写就行。
// webpack.common.js const path = require('path'); const fs = require('fs'); const webpack = require('webpack'); const entry = {}; ... // 遍历src/entry文件夹下的一级js文件做打包入口,即entry/*.js const webConfig = { entry, output: { filename: '[name].web.js', path: path.resolve(__dirname, 'dist'), publicPath: 'dist/' }, module: { rules: [ { test: /\.js$/, use: ['babel-loader'] }, { test: /\.vue(\?[^?]+)?$/, use: [ { loader: 'vue-loader', options: { ... // 这个地方的配置参考 https://github.com/weexteam/weex-vue-render } } ] } ] }, plugins: [] }; const weexConfig = { entry, output: { filename: '[name].js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.js$/, use: ['babel-loader'] }, { test: /\.vue(\?[^?]+)?$/, use: ['weex-loader'] } ] }, plugins: [ // 打包后的.weex.js的头部加上以下banner才能被native识别 new webpack.BannerPlugin({ banner: '// { "framework": "Vue" }\n"use weex:vue";\n', raw: true }) ] }; module.exports = [webConfig, weexConfig];
打包编译js分开发环境和生成环境,生成环境,直接打包webpack.common.js的配置就行,每一个页面会打包出对应的.web.js和.weex.js。
// webpack.prod.conf.js const path = require('path'); const rimraf = require('rimraf'); const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); const commonConfig = require('./webpack.common'); const [webConfig, weexConfig] = commonConfig; webConfig.plugins.push(new UglifyJSPlugin()); weexConfig.plugins.unshift(new UglifyJSPlugin()); // 是unshift,要先压缩js,后加banner rimraf.sync(path.resolve(__dirname, 'dist')); // 删除dist文件夹 module.exports = [webConfig, weexConfig];
开发环境时,我们会起一个devServer,server在内存中会生成对应的.web.js,但是此时还需要生成.weex.js,因为开发的时候我们不仅要在web上看效果,还要在native端看效果。
// webpack.dev.conf.js ... const weexConfig = webpackMerge(commonConfig[1], { watch: true }); // 以Node.js API的方式执行webpack生成weex.js,https://webpack.js.org/api/node/ webpack(weexConfig, (err, stats) => { if (err) { console.err('COMPILE ERROR:', err.stack); } }); const webConfig = webpackMerge(commonConfig[0], { devServer: { ... } }); // 寻找可用的端口号 portfinder.getPort((err, port) => { if (err) { console.log(err); } else { webConfig.devServer.port = port; } }); module.exports = webConfig;
如果一个工程只能用来开发一张页面未免太奢侈了,所以webpack打包entry配置的是多入口,这样就可以在一个工程里面开发多个页面,想查看不同的页面时,修改一下浏览器的url即可,基于此,做出了以下改动:
// index.html ... <script> // 这段js的意思是:默认加载dist文件夹下的index.web.js,如果想查看另一个页面,把url中的page=index.web.js改成其他的js即可 // 比如page=home.web.js,此时查看的就是home.vue的内容 ;(function () { var defaultPage = 'index.web.js' var match = location.search.match(new RegExp('[?|&]page=([^&]+)')) var page = match && match[1] if (!page) { return location.href = location.href.replace(/\?|$/, function (f) { var query = '?page=' + defaultPage return f ? query + '&' : query }) } var $script = document.createElement('script') $script.src = './dist/' + page document.body.appendChild($script) })(); </script>
此时目录结构如下(忽略node_modules):
. ├── .babelrc ├── index.html ├── package-lock.json ├── package.json ├── src │ ├── entry │ │ ├── home.js │ │ └── index.js │ └── page │ ├── home.vue │ └── index.vue ├── webpack.common.js ├── webpack.dev.conf.js └── webpack.prod.conf.js
其实,开发weex页面的工程已经完成了,我们实现了一个.vue文件可以打包编译成.web.js和.weex.js。但是,少了一个环节有没有觉察到?——打包出来的.weex.js在native上怎么看页面效果。
native预览
在开发代码时,我们已经动态生成了.weex.js,现在要做的就是把.weex.js以二维码的形式展示出来。
现在是这么简单处理的:在当前页面的左上角上显示了一个二维码,这个二维码的内容正是当前页面对应的.weex.js,然后用 Weex playground app 扫描这个二维码就可以看到页面在手机上渲染的真实效果。
如果觉得这个二维码在开发的时候有点碍事,想开发一段时间之后再看native预览效果,那你可以先把二维码隐藏掉。
目前的这个效果只是达到了native预览的目的,但是还有很大提升空间。我最终的想法是这样的:页面上有一个类似于苹果手机的虚拟Home键,可以随意拖动,当鼠标hover它上面的时候,会以popover类似的效果展示出二维码。
由于时间比较紧,这个效果并没有开发出来,欢迎各位能够提PR,共同完善这个weex-starter工程。github:https://github.com/jasonintju/weex-starter
Weex小册系列:
Weex小册—认识一下Weex
Weex小册—从0搭建一个Weex项目
...未完待续
附招聘:
杭州阿里淘宝技术部基础平台招P6/P7前端。
你可能不知道基础平台部,但是你可能听过Weex、Atlas、无人超市、EMAS等,这里有顶尖的架构师团队、移动AI团队、算法团队、前端团队、产品技术团队,我们现在做的是EMAS跨平台产品,欢迎优秀的前端工程师加入我们,简历发送至 [email protected],当然如果你有其他心仪的部门,我也可以帮忙内推。招聘详情
附我们美丽的产品小姐姐照片,欢迎来撩啊~