Train of thought
We want to understand the implementation principle of WEEX from weeX Runtime source code.
runtime
entries
The entries folder is the location of the entry file definition. We clicked on the definition of its index.js file and found that it is the entry file for various front-end frameworks, using the index.js entry to compile all framework versions.
import * as Vanilla from './vanilla/index'
import * as Vue from 'weex-vue-framework'
import * as Weex from './legacy/index'
import Rax from 'weex-rax-framework'
export default {
Vanilla,
Vue,
Rax,
Weex
}
Copy the code
For example vue. Js:
import setup from './setup'
import * as Vue from 'weex-vue-framework'
setup({ Vue })
Copy the code
api
The API folder mainly exposes apis to the front-end framework for injecting VUE. This includes initializing the third-party framework and passing in some objects in the config parameter provided by WEEx. Returns global APIS for third-party front-end frameworks.
constconfig = { Document, Element, Comment, Listener, TaskCenter, sendTasks (... args) {// Call native code
if (typeof callNative === 'function') {
returncallNative(... args) }return (global.callNative || (() = >{})) (... args) } } Document.handler = config.sendTasksCopy the code
The third-party front-end framework can be initialized based on the parameters in Config. Exposed global apis are called by third-party front-end frameworks
createInstance,// Create an instance
createInstanceContext,// Create a power context object
getRoot,// Get the root instance
getJSFMVersion,// Get the JSM version
getDocument: getDoc,// Get the current Document global object
registerService: register,// Register service
unregisterService: unregister,// Cancel registration
callJS (id, tasks) {//callJS, native uses this method to callJS methods uniformly
const framework = frameworks[getFrameworkType(id)]
if (framework && typeof framework.receiveTasks === 'function') {
return framework.receiveTasks(id, tasks)
}
return receiveTasks(id, tasks)
}
adaptMethod('registerComponents', registerComponents)// Register the component
adaptMethod('registerModules', registerModules)// Register module
adaptMethod('registerMethods');
['destroyInstance'.'refreshInstance'].forEach(genInstance)// Register life cycle functions
Copy the code
The initHandler function that initializes the TaskCenter is the brige/ Taskcenter. js init function that mounts external methods on the TaskCenter prototype. These methods are ultimately handled by the native methods of the invocation.
const DOM_METHODS = {
createFinish: global.callCreateFinish,
updateFinish: global.callUpdateFinish,
refreshFinish: global.callRefreshFinish,
createBody: global.callCreateBody,
addElement: global.callAddElement,
removeElement: global.callRemoveElement,
moveElement: global.callMoveElement,
updateAttrs: global.callUpdateAttrs,
updateStyle: global.callUpdateStyle,
addEvent: global.callAddEvent,
removeEvent: global.callRemoveEvent,
__updateComponentData: global.__updateComponentData
}
Copy the code
WeexInstance is the weeX interface exposed to developers. We can view the globally accessible interface from its constructor:
constructor (id, config, data) {
setId(this.String(id))
this.config = config || {}// Global configuration, including environment variables, device information, etc
this._nativeData = data || {}// Initialize the data passed in natively
this.document = new Document(id, this.config.bundleUrl)// Simulate the DOM document object
this.requireModule = this.requireModule.bind(this)// Import modules
this.importScript = this.importScript.bind(this)// Import js code, execute immediately
this.isRegisteredModule = isRegisteredModule // Check whether the module exists
this.isRegisteredComponent = isRegisteredComponent // Check whether the component exists
}
Copy the code
These are the weeX interface we can directly call.
brige
Callbackmanager.js: callback function manager, including component hook function Handler: task processor, used to call native methods
// callable native methods
const handlerMap = {
createBody: 'callCreateBody'.addElement: 'callAddElement'.removeElement: 'callRemoveElement'.moveElement: 'callMoveElement'.updateAttrs: 'callUpdateAttrs'.updateStyle: 'callUpdateStyle'.addEvent: 'callAddEvent'.removeEvent: 'callRemoveEvent'
}
Copy the code
Listener: An external access interface (wrapper) used by the Handler to provide batching operations for calling native methods or sending call signals. Receiver.js: Receives natively sent events and component callbacks
export function receiveTasks (id, tasks) {
const document = getDoc(id)
if (!document) {
return new Error(`[JS Framework] Failed to receiveTasks, `
+ `instance (${id}) is not available.`)}if (Array.isArray(tasks)) {
return tasks.map(task= > {
switch (task.method) {
case 'callback': return callback(document. task.args)case 'fireEventSync':
case 'fireEvent': return fireEvent(document. task.args)case 'componentHook': return componentHook(document. task.args) } }) } }Copy the code
TaskCenter: a task processing center that handles the following tasks:
- Callbacks are managed through CallbackManager
- Manage hook callbacks through CallbackManager
- Handle DOM operations (by sending instructions to native)
- Handle component method calls
- Handle module method calls
Where the callback-related functions and hook callbacks are managed through callbackManager, dom operations are sent to the native by calling global.callNative in sendTasks on the global Config object. Component and Module are handled by their respective handlers, callNativeComponent and callNativeModule methods defined on global, respectively. If they don’t exist, sendTasks is also called.
vdom
Index.js: entry file
Commen.js: comment node
Document: vDOM implementation of the corresponding browser Document
Element: VDOM implementation of Element nodes
Node.js: The base implementation of vDOM, the base class
Weexelement. js: a file used to register weex components. It has a map collection that stores all registered components and adds corresponding methods to the components.
Operation.js: All dom manipulation methods are defined in this file
Mind mapping
According to the above arrangement and understanding, the knowledge points will be sorted out in the form of mind mapping to make the knowledge points related and the context clearer, which is convenient for us to understand and memorize.
Above is our runtime-JS framework comb and understanding, next we read weex-VUE-framework principle to further understand the use of this part of the code.
weex-vue-framework
First of all, weeX-VUE-Framework does the following things:
-
createInstanceContext
Create an instance to process the WEEX instance
-
createVueModuleInstance
Create a Vue instance and adapt it to weeX
-
Mount apis provided by WEEX to Vue instances, such as Document and taskCenter
Weex js runtime framework actually provides a series of apis for the front-end framework, including DOM manipulation, event processing, etc. Initialize front-end frameworks such as Vue through encapsulated VDOM operations, event management systems, services, etc. Weex native initialization weexJS runtime runs in v8 engine, equivalent to our JS runtime environment has a global weEXJS runtime declaration of the Vue constructor (Vue front-end instance initialization), weEXJS runtime Vue instance is different from the Web end, it is special processing, The main difference is that its VDOM operations are not browser DOM operations, but natively encapsulated corresponding View operations exposed to it through the WeexJS runtime; The event architecture is based on the event architecture exposed to it by the native encapsulation. In fact, we can write our own front-end frameworks or adapt existing ones to fit the WeexJS runtime framework (mainly VDOM and the event system). Need to define the init on the frame (config), createInstance (config, weexInstanceContext), receiveTasks (such as id, task) prototype method based on weexjs runtime exposed some of the initialization processing API.
Further understanding
According to this idea, we look at the source code of Vue compiled to understand, we write Vue file compiled by weex-Loader or Weex-Builder is the following form:
(function(modules){
var installedModules={};
function _webpack_require_(moduleId){
// Check if module is in cache
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = installedModules[moduleId] = {
id: moduleId,
loaded: false.exports: {}};// Execute the module function
modules[moduleId].call(module.exports, module.module.exports, __webpack_require__);
// Flag the module as loaded
module.loaded = true;
// Return the exports of the module
return module.exports;
}
// omit extraneous code......
return __webpack_require__(__webpack_require__.s = 647); }) (// omit extraneous code......
647: (function(module.exports, __webpack_require__) {
var __vue_exports__, __vue_options__;
var __vue_styles__ = [];
/* styles Generates style-related data */
__vue_styles__.push(__webpack_require__(648));/* script generates vue options */
__vue_exports__ = __webpack_require__(649);
/* The render function generated by template */
var __vue_template__ = __webpack_require__(651);
__vue_options__ = __vue_exports__ = __vue_exports__ || {};
// The following __vue_options__ operation is equivalent to __vue_exports__
__vue_options__.render = __vue_template__.render;
__vue_options__._scopeId = 'data-v-07574e69';
__vue_options__.style = __vue_options__.style || {};
__vue_styles__.forEach(function (module) {
for (var name in module) {
__vue_options__.style[name] = module[name]; }});module.exports = __vue_exports__;
module.exports.el = 'true';
// The options passed in is an option object to compile the template into the render function
// The following is the initialization of the Vue, data listening, mounting the VDOM node to create a real view
new Vue(module.exports);
}),
/ / style
648: (function(module.exports) {
module.exports = {
'deleteImg': {
'position': 'absolute'.'width': '36'.'height': '36'.'top': '37'.'right': '20'
},
'deleteImg1': {
'position': 'absolute'.'width': '36'.'height': '36'.'top': '37'.'right': '65'
},
'deleteImg2': {
'position': 'absolute'.'width': '36'.'height': '36'.'top': '37'.'right': '20'
},
'bgt': {
'width': '750'
},
'img': {
'width': '100'.'height': '100'
},
'topimg': {
'width': '520'.'height': '160'.'position': 'absolute'.'top': '180'.'left': '115'
},
'bgimg': {
'width': '750'.'marginTop': '148'.'alignItems': 'center'.'textAlign': 'center'.'flexDirection': 'column'.'justifyContent': 'center'
},
'inputdiv': {
'marginLeft': '50'.'width': '650'.'height': '100'.'flexDirection': 'row'.'borderBottomWidth': '1'.'borderBottomColor': '#d3d3d3'.'borderBottomStyle': 'solid'.'paddingTop': '30'
},
'inputimg': {
'fontSize': '30'.'color': '# 333333'.'width': '100'.'lineHeight': '50'
},
'content': {
'marginTop': '140'
},
'input': {
'width': '500'.'height': '50'.'marginLeft': '30'
},
'textchar': {
'fontSize': 'and'.'color': '#ffffff'.'fontWeight': 'bold'
},
'btn': {
'marginTop': '100'.'marginLeft': '40'.'backgroundColor': '#2250FF'.'width': '670'.'height': '86'.'justifyContent': 'center'.'alignItems': 'center'.'borderRadius': '43'
},
'btns': {
'marginTop': '30'.'marginLeft': '40'.'width': '670'.'flexDirection': 'row'.'justifyContent': 'space-between'
},
'lichar': {
'color': '#2250FF'.'fontSize': '26'}}; }),// export vue options with js code
/ * * * / 649:
/ * * * / (function(module.exports, __webpack_require__) {
'use strict';
var _updateDialog = __webpack_require__(39);
var _updateDialog2 = _interopRequireDefault(_updateDialog);
var _type_net = __webpack_require__(17);
var _type_net2 = _interopRequireDefault(_type_net);
var _configEnv = __webpack_require__(2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var notify = weex.requireModule('notify');
var nav = weex.requireModule('navigator');
var pref = weex.requireModule('pref');
var utils = weex.requireModule('utils');
var st = weex.requireModule('static');
var modal = weex.requireModule('modal');
var updater = weex.requireModule('updater');
var config = __webpack_require__(650);
exports.default = {
components: {
UpdateDialog: _updateDialog2.default
},
data: {
userInfo: {},
remember: false.userName: ' '.password: ' '.dialogShow: false.pogressShow: false.nowVertion: ' '.must: false.updateInfo: {},
value: 0.progress: 0.showPwd: false
},
computed: {
showName: function showName() {
if (this.userName == ' ') {
return false;
} else {
return true; }},delPwd: function delPwd() {
if (this.password == ' ') {
return false;
} else {
return true; }}},methods: {
// thirdLogin(){
// modal.toast({message:' under development ~~'})
// },
// ksdl(){
// modal.toast({message:' under development ~~'})
// },
deleteImg: function deleteImg(index) {
if (index == 1) {
this.userName = ' ';
} else {
this.password = ' '; }},cencels: function cencels() {
this.dialogShow = false;
},
confirms: function confirms() {
var self = this;
self.update();
this.dialogShow = false;
},
login: function login() {
// Remember the password
var self = this;
utils.getPhoneInfo(function (res) {
self.netLogin(res);
});
// var info={
// brand:'111',
// phoneModel:'1',
// osName:'1',
// osVersion:'1'
// };
// self.netLogin(info);
},
netLogin: function netLogin(deviceInfo) {
var self = this;
var nav = weex.requireModule('navigator');
var rsaAuthInfo = utils.encryptAuthInfo(self.userName, self.password).authInfo;
_type_net2.default.post(_configEnv.env.login, { authorization: rsaAuthInfo, clientCode: 'mb-app'.equipmentBrand: deviceInfo.brand, equipment: deviceInfo.phoneModel, systemName: deviceInfo.osName,
systemVersion: deviceInfo.osVersion }, function (res) {
var data = res.res;
if (data.code == 0) {
modal.toast({ message: 'Successful landing' });
pref.set('userInfo', data.data.userContext);
st.set('userInfo', data.data.userContext);
pref.set('key', { key: rsaAuthInfo });
var token = ' ';
if (weex.config.env.platform === 'iOS') {
token = res.headers['Authorization'];
} else {
token = res.token;
}
// This code needs to be added to the workbench
// utils.setSchema('assets');
st.setPUserInfo({ key: rsaAuthInfo, token: token, data: data.data.userContext.currentUser });
nav.pushParam('host.js', { from: 'login' });
utils.JPushLogin(_configEnv.env.env + '_' + 'mobile_' + data.data.userContext.currentUser.userCode);
} else {
modal.toast({ message: data.msg }); }},'login'); }},created: function created() {
var self = this;
if(weex.config.env.platform ! ='iOS') {
notify.regist('login_dismiss'.function (res) {
nav.dismiss();
});
}
var globalEvent = weex.requireModule('globalEvent');
globalEvent.addEventListener('onPageInit'.function (e) {
self.userInfo = pref.get('userInfo');
var notify = weex.requireModule('notify');
notify.send('guide_dismiss', { a: '1' });
notify.send('index_dismiss', { a: '1'}); }); }}; }),// Render function generated from the template template
/ * * * / 651:
/ * * * / (function(module.exports) {
module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return _c('div', {
staticClass: ['bgt']
}, [_vm._m(0), _c('div', {
staticClass: ['content']
}, [_c('div', {
staticClass: ['inputdiv']
}, [_c('text', {
staticClass: ['inputimg']
}, [_vm._v('account')]), _c('input', {
staticClass: ['input'].attrs: {
'placeholder': 'Enter account number'.'value': (_vm.userName)
},
on: {
'input': function($event) {
_vm.userName = $event.target.attr.value;
}
}
}), (_vm.showName) ? _c('image', {
staticClass: ['deleteImg'].attrs: {
'src': 'root:img/delete.png'
},
on: {
'click': function($event) {
_vm.deleteImg(1);
}
}
}) : _vm._e()]), _c('div', {
staticClass: ['inputdiv']
}, [_c('text', {
staticClass: ['inputimg']
}, [_vm._v('password')]), (!_vm.showPwd) ? _c('input', {
staticClass: ['input'].attrs: {
'placeholder': 'Enter your password'.'type': 'password'.'value': (_vm.password)
},
on: {
'input': function($event) {
_vm.password = $event.target.attr.value;
}
}
}) : _c('input', {
staticClass: ['input'].attrs: {
'placeholder': 'Enter your password'.'value': (_vm.password)
},
on: {
'input': function($event) {
_vm.password = $event.target.attr.value;
}
}
}), (_vm.showPwd && _vm.delPwd) ? _c('image', {
staticClass: ['deleteImg1'].attrs: {
'src': 'root:img/user/see.png'
},
on: {
'click': function($event) {
_vm.showPwd = !_vm.showPwd;
}
}
}) : (!_vm.showPwd && _vm.delPwd) ? _c('image', {
staticClass: ['deleteImg1'].attrs: {
'src': 'root:img/user/noSee.png'
},
on: {
'click': function($event) { _vm.showPwd = ! _vm.showPwd; } } }) : _vm._e(), (_vm.delPwd) ? _c('image', {
staticClass: ['deleteImg'].attrs: {
'src': 'root:img/delete.png'
},
on: {
'click': function($event) {
_vm.deleteImg(2);
}
}
}) : _vm._e()])]), _c('div', {
staticClass: ['btn'].on: {
'click': _vm.login
}
}, [_c('text', {
staticClass: ['textchar']
}, [_vm._v('login')]]]]); },staticRenderFns: [function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return _c('div', {
staticClass: ['bgimg']
}, [_c('image', {
staticClass: ['img'].attrs: {
'src': 'root:img/logo1.png'
}
}), _c('text', {
staticStyle: {
fontSize: '34px'.fontWeight: 'bold'.marginTop: '30px'
}
}, [_vm._v('Handheld workbench')]]); }};module.exports.render._withStripped = true;
})
// omit extraneous code......
)
Copy the code
The js code compiled by this end is first of all a module loading implementation, related to module loading understanding before in the understanding of webpack module loading principle introduced, here is no longer described, here focuses on weeX principle related. We see that the last line of the self-executing function executes __webpack_require__(__webpack_require__.s = 647), which loads the object with id 647 in the passed argument, and we find it:
647: (function(module.exports, __webpack_require__) {
var __vue_exports__, __vue_options__;
var __vue_styles__ = [];
/* styles Generates style-related data */
__vue_styles__.push(__webpack_require__(648));/* script generates vue options */
__vue_exports__ = __webpack_require__(649);
/* The render function generated by template */
var __vue_template__ = __webpack_require__(651);
__vue_options__ = __vue_exports__ = __vue_exports__ || {};
// The following __vue_options__ operation is equivalent to __vue_exports__
__vue_options__.render = __vue_template__.render;
__vue_options__._scopeId = 'data-v-07574e69';
__vue_options__.style = __vue_options__.style || {};
__vue_styles__.forEach(function (module) {
for (var name in module) {
__vue_options__.style[name] = module[name]; }});module.exports = __vue_exports__;
module.exports.el = 'true';
// The options passed in is an option object to compile the template into the render function
// The following is the initialization of the Vue, data listening, mounting the VDOM node to create a real view
new Vue(module.exports);
}),
Copy the code
So the Jsbundle will first execute the code that attaches the Render function to the VUE option and then creates a Vue instance using options. Vue initialization, data listening, mounting vDOM nodes to create real views and other internal Vue operations, but here the VDOM operations to create a view is not browser DOM operations but native encapsulated view creation operations.
Weex framework is understood in layers
-
Vue.js, rax. js: front-end framework
-
Weex-js-frameworlk: calls the C++native interface, using JSBrige to call Android and IOS, encapsulating a series of dom operations. TaskCenter manages call-back, module and component methods.
-
Weexsdk (c++) : weex’s c++ engine, used to connect Android and ios, encapsulates the unified dom system, callback event management, module, component management and call.
-
Android/ios: native UI, components, function modules, etc.
conclusion
The implementation principle of WEEX is as follows:
- Firstly, we use weexsdk(c++) to encapsulate Android and IOS UI into a unified vdom and event system.
- Run-time jsFramework is a further encapsulation and implementation of this vdom and event system. It can directly call native code to interact with Android and IOS platforms. We will run the runtime of jsFramework when weexSdk is initialized. This is equivalent to initializing the JS execution environment and adding the WEEX framework API globally.
- Finally, there are front-end frameworks such as vue.js and rax.js, which can use the VDOM and event architecture encapsulated by the Run-time jsFramework, just as we do on the browser side.
Weex runtime-JSFramework and WEEX-VUE-Framework source code after weeX implementation principle summary, if helpful to you, also hope to praise, if there is a mistake, also hope to correct, thank you ~~~ ~