“> < p style =” max-width: 100%; clear: both; min-height: 1em;
Story 1: Build
Although the slogan of WEEX is write and run at one time, the build process is actually different. Weex-loader is used for native build, while VUe-loader is used for Web build. In addition, there are many differences, so WebPack requires two sets of configuration.
Best practices
Use WebPack to generate two bundles, one is a Vue-Router based Web SPA and the other is a native multi-portal BundleJS
First suppose we develop a bunch of pages under SRC/Views
Build web configuration
The entry file on the Web side is render.js
import weexVueRenderer from 'weex-vue-render'
Vue.use(weexVueRenderer)Copy the code
main.js
import App from './App.vue'
import VueRouter from 'vue-router'
import routes from './routes'
Vue.use(VueRouter)
var router = new VueRouter({
routes
})
/* eslint-disable no-new */
new Vue({
el: '#root',
router,
render: h= > h(App)
})
router.push('/')Copy the code
App.vue
<template>
<transition name="fade" mode="out-in">
<router-view class=".container" />
</transition>
</template>
<script>
export default {
// ...
}
</script>
<style>
// ...
</style>Copy the code
Webpack. Prod. Conf. Js entrance
const webConfig = merge(getConfig('vue'), {
entry: {
app: ['./src/render.js'.'./src/app.js']},output: {
path: path.resolve(distpath, './web'),
filename: 'js/[name].[chunkhash].js',
chunkFilename: 'js/[id].[chunkhash].js'},...module: {
rules: [{test: /\.vue$/,
loader: 'vue-loader'}]}})Copy the code
Build native configuration
The packaging process of the native end is actually to export each. Vue file under SRC /views into a separate vue instance, which can be realized by writing a Node script
// build-entry.js
require('shelljs/global')
const path = require('path')
const fs = require('fs-extra')
const srcPath = path.resolve(__dirname, '.. /src/views') // Each.vue page
const entryPath = path.resolve(__dirname, '.. /entry/') // The folder where the entry files are stored
const FILE_TYPE = '.vue'
const getEntryFileContent = path= > {
return '// import App from'${path}${FILE_TYPE}'
/* eslint-disable no-new */
new Vue({
el: '#root',
render: h => h(App)
})
`
}
// Export method
module.exports = _= > {
// Delete the original directory
rm('-rf', entryPath)
// Write entry files for each file
fs.readdirSync(srcPath).forEach(file= > {
const fullpath = path.resolve(srcPath, file)
const extname = path.extname(fullpath)
const name = path.basename(file, extname)
if (fs.statSync(fullpath).isFile() && extname === FILE_TYPE) {
// Write to the vue render instance
fs.outputFileSync(path.resolve(entryPath, name + '.js'), getEntryFileContent('.. /src/views/' + name))
}
})
const entry = {}
// Add multiple entries
fs.readdirSync(entryPath).forEach(file= > {
const name = path.basename(file, path.extname(path.resolve(entryPath, file)))
entry[name] = path.resolve(entryPath, name + '.js')})return entry
}Copy the code
Multiple entries are generated and packaged in webpack.build.conf.js
const buildEntry = require('./build_entry')
// ..
/ / weex configuration
const weexConfig = merge(getConfig('weex'), {
entry: buildEntry(), // Write multiple entries
output: {
path: path.resolve(distPath, './weex'),
filename: 'js/[name].js' // The WEEX environment does not use hash names
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'weex-loader'}]}})module.exports = [webConfig, weexConfig]Copy the code
The final result
Story 2: Using a preprocessor
In a vUE single file, we can configure the preprocessor in vue-loader as follows
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
scss: 'vue-style-loader! css-loader! sass-loader', // <style lang="scss">
sass: 'vue-style-loader! css-loader! sass-loader? indentedSyntax' // <style lang="sass">}}}Copy the code
Weex in native environment actually processed CSS into JSON loading module, so…
- use
vue-loader
The configured preprocessor is displayed normally in the Web environmentnative
Is invalid - There is no global style in native environment, in JS file
import 'index.css'
It’s not valid
Problem One
After studying the weex-Loader source code, it is found that vue does not need to display the loader configuration. Just specify
<style lang="sass">
@import './index.scss'
</style>Copy the code
Syntax highlighting, perfect!
Problem Two
Although there is no concept of global styles, separate import style files are supported
<style lang="sass">
@import './common.scss'
@import './variables.scss'
// ...
</style>Copy the code
Story 3: Style differences
This has been described in some detail in official documentation, but there are a few points worth noting
shorthand
Margin: 0, 0, 10px 10px is not supported
The background color
Note that android views have a default color of white, while iOS does not have a default color if it is not set
Floating point error
Weex uses 750px * 1334px as the default size. In actual rendering, due to floating point error, there may be a few px error, such as fine lines, etc. You can add or subtract several px to debug
Nested writing
Even with a preprocessor, CSS nesting can cause style failures
Story 4: Page jump
There are three types of page redirection in WEEx
-
native -> weex
:weex
The page needs a controller as a container, and that’s itnative
The jump between -
weex -> native
: Required to passmoduleForm jumps by sending events to native -
Weex -> weex: Use the Navigator module, assuming the two WEEX pages are A.js and B.js, to define mixin methods
function isWeex () { return process.env.COMPILE_ENV === 'weex' // Need to be customized in webpack } export default { methods: { push (path) { if (isWeex()) { const toUrl = weex.config.bundleUrl.split('/').slice(0, -1).join('/') + '/' + path + '.js' // Change the absolute address of a.js to the absolute address of b.js weex.requireModule('navigator').push({ url: toUrl, animated: 'true'})}else { this.$router.push(path) / / to use vue - the router}},pop () { if (isWeex()) { weex.requireModule('navigator').pop({ animated: 'true'})}else { window.history.back() } } } }Copy the code
So the component uses this.push(URL), this.pop() to jump to
Jump configuration
-
IOS page redirecting does not need to be configured, but Android does. The project generated by using Weexpack Platform Add Android has been configured, but the official documentation does not explain how to access existing applications
-
In Android, intent-filter is used to block jumps
<activity android:name=".WXPageActivity" android:label="@string/app_name" android:screenOrientation="portrait" android:theme="@android:style/Theme.NoTitleBar"> <intent-filter> <action android:name="android.intent.action.VIEW"/> <action android:name="com.alibaba.weex.protocol.openurl"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="com.taobao.android.intent.category.WEEX"/> <data android:scheme="http"/> <data android:scheme="https"/> <data android:scheme="file"/> </intent-filter> </activity>Copy the code
-
Then we create a new WXPageActivity to proxy all weeX page rendering. The core code is as follows
@Override protected void onCreate(Bundle saveInstanceState) { // ... Uri uri = getIntent().getData(); Bundle bundle = getIntent().getExtras(); if(uri ! =null) { mUri = uri; } if(bundle ! =null) { String bundleUrl = bundle.getString("bundleUrl"); if (!TextUtils.isEmpty(bundleUrl)) { mUri = Uri.parse(bundleUrl); } } if (mUri == null) { Toast.makeText(this."the uri is empty!", Toast.LENGTH_SHORT).show(); finish(); return; } String path = mUri.toString(); // There is always a bug in the url argument with HTTP :/ String jsPath = path.indexOf("weex/js/") > 0 ? path.replace("http:/"."") : path; HashMap<String, Object> options = new HashMap<String, Object>(); options.put(WXSDKInstance.BUNDLE_URL, jsPath); mWXSDKInstance = new WXSDKInstance(this); mWXSDKInstance.registerRenderListener(this); mWXSDKInstance.render("weex", WXFileUtils.loadAsset(jsPath, this), options, null, -1, -1, WXRenderStrategy.APPEND_ASYNC); }Copy the code
-
By the way… Weex does not officially provide customizable NAV components which is really inconvenient.. It is often necessary to bridge native through modules to achieve jump requirements
Story 5: Data transfer between pages
-
native -> weex
: can be innative
End callsrender
When the incomingoption
, for exampleNSDictary *option = @{@"params": @{}}
In theweex
The use ofweex.config.params
Take out the data -
weex -> weex
Use:storage -
weex -> native
Use:The custom module
Story 6: Picture loading
The official website mentions how to load web images, but the behavior of loading local images is definitely inconsistent for the three ends, which means we have to change the path of referencing images to Native again and then package.
But of course there are solutions
- Step 1
webpack
It’s easy to set up the image resource to be packaged separatelybundleJs
The image path to access becomes/images/..
{
test: /\.(png|jpe? g|gif|svg)$/, loader: 'url-loader', query: { limit:1,
name: 'images/[hash:8].[name].[ext]'
}
}Copy the code
- Step 2 So now we’re going toJs folder and images folder in the same directoryIn the
native
In iOSmainBundle
, Android generally putsrc/main/assets
, the next step is toimgloader
Interface extension to replace the local resource path code is ok
The iOS code is as follows:
- (id<WXImageOperationProtocol>)downloadImageWithURL:(NSString *)url imageFrame:(CGRect)imageFrame userInfo:(NSDictionary *)options completed:(void(^) (UIImage *, NSError *, BOOL))completedBlock{
if ([url hasPrefix:@ "/ /"]) {
url = [@"http:" stringByAppendingString:url];
}
// Load the local image
if ([url hasPrefix:@"file://"]) {
NSString *newUrl = [url stringByReplacingOccurrencesOfString:@"/images/" withString:@ "/"];
UIImage *image = [UIImage imageNamed:[newUrl substringFromIndex:7]];
completedBlock(image, nil.YES);
return (id<WXImageOperationProtocol>) self;
} else {
// Load the network image
return (id<WXImageOperationProtocol>)[[SDWebImageManager sharedManager]downloadImageWithURL:[NSURL URLWithString:url] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if(completedBlock) { completedBlock(image, error, finished); }}]; }}Copy the code
The Android code is as follows:
@Override
public void setImage(final String url, final ImageView view,
WXImageQuality quality, final WXImageStrategy strategy) {
WXSDKManager.getInstance().postOnUiThread(new Runnable() {
@Override
public void run(a) {
if(view==null||view.getLayoutParams()==null) {return;
}
if (TextUtils.isEmpty(url)) {
view.setImageBitmap(null);
return;
}
String temp = url;
if (url.startsWith("/ /")) {
temp = "http:" + url;
}
if (temp.startsWith("/images/")) {
// Filter out all relative positions
temp = temp.replace(".. /"."");
temp = temp.replace(". /"."");
// Replace the asset directory configuration
temp = temp.replace("/images/"."file:///android_asset/weex/images/");
Log.d("ImageAdapter"."url:" + temp);
}
if (view.getLayoutParams().width <= 0 || view.getLayoutParams().height <= 0) {
return;
}
if(! TextUtils.isEmpty(strategy.placeHolder)){ Picasso.Builder builder=new Picasso.Builder(WXEnvironment.getApplication());
Picasso picasso=builder.build();
picasso.load(Uri.parse(strategy.placeHolder)).into(view);
view.setTag(strategy.placeHolder.hashCode(),picasso);
}
Picasso.with(WXEnvironment.getApplication())
.load(temp)
.into(view, new Callback() {
@Override
public void onSuccess(a) {
if(strategy.getImageListener()! =null){
strategy.getImageListener().onImageFinish(url,view,true.null);
}
if(!TextUtils.isEmpty(strategy.placeHolder)){
((Picasso) view.getTag(strategy.placeHolder.hashCode())).cancelRequest(view);
}
}
@Override
public void onError(a) {
if(strategy.getImageListener()! =null){
strategy.getImageListener().onImageFinish(url,view,false.null); }}}); }},0);
}Copy the code
Story 7: Practice in the production environment
Incremental updating
Google-diff-match-patch can be used to achieve this. Google-diff-match-patch has many language versions. The idea is as follows:
- The server builds a management front end
bundlejs
System to provide queriesbundlejs
Version and download API - First client access
weex
Page to the server to downloadbundlejs
file - During each client initialization, the server is silently visited to determine whether it needs to be updated. If it needs to be updated, the server side
diff
The differences between the two versions are returneddiff
.native
End usepatch api
Generate a new version ofbundlejs
down-cycled
Generally, a web interface is deployed at the same time. If a bug occurs on the WEEX page, use the WebView to load the Web version. It is recommended to rely on the server API to control degraded switchover
conclusion
Weex advantage: Rely on VUE, easy to get started. It can meet the requirements of vUE technology-led companies to provide simple/less low-level interaction/hot update pages for Native dual ends
Weex weakness: tweaking at Native is a constant pain in my heart.. As well as the well-known ecological issues, the maintenance team did not spend much effort to answer the community’s questions, and the official document had so many errors that I made a few PR’s while reading it
For the problems not mentioned in the article, welcome to discuss with the author, or refer to my WEEx-start-kit, of course, a star is also very good