UI Componentization Engineering Practice (WEEX VUE)

Update at 2020.07.29 Hangzhou Youzan e-commerce team is in urgent need of 10+ HC, covering front-end, Java, testing, interested welcome to contact [email protected] or directly contact WX: WSLDD225

preface

The front end team of Shangmakeup has been using WEEX for the unified development of three terminals for a period of time. By the time of publication of this article, most pages of “Dendandian” APP have been reconstructed with WEEX, and some basic components and business components have also been accumulated during this period.

The previous way to maintain components was to maintain a Components folder in the project of The Dendshop project. With the daily development iterations and the increase of parallel requirements and developers, some problems were exposed in this maintenance way.

1. Developers are free to modify components within components, break agreed specifications, or bury bugs as required.

2. There is A lack of specification for defining components. For example, in the development of A requirement, the developer of A thought that this function could be separated into components and directly defined and used in components, but it is actually A false requirement.

3. Component separation process cannot be used together. For example, A developer cut A feature branch Feature /A and selected A general component ComponentA according to the project; B developer cut A feature branch B and also wanted to use this ComponentA component.

4.

Due to the above inconvenience, we tried to pull components out and put them into an internal private NPM repository to be maintained as NPM packages.

That is, we will spon-UI (internal component library name) as a separate project to maintain, constraints form a component library development specification, can effectively solve the above problems.

This article is the practice of the extraction process, including component debugging, document debugging, NPM use, component release and so on. Of course weeX syntax is the same as VUE syntax, and these practices also apply to VUE.

1. Debug the component library

Take a look at the directory structure of the SPon-UI component library project.

|- spon-ui ||-- build ||-- docs ||-- examples ||-- packages |||--- weex-field ||||---- index.js ||||---- field.vue | | | | -- example. Vue | | | | -- readme. Md | | | | -- package. Json | | - SRC duplicate codeCopy the code
  • Build stores some script execution files for project debugging and release.
  • Docs stores the script for document debugging and generates a document debugging server.
  • Examples contains component debugging scripts to generate a component debugging server. (Does not store component examples)
  • Packages holds real components, along with documentation and examples of components.
  • SRC holds the public methods that a component can use.

Component debugging

The examples folder contains scripts for component debugging. This folder does not need to be changed during the build process, but defines the debug server logic. Does not contain real component examples.

In example.vue, the vue components in the current directory are introduced. Debugging is for example.vue, because debugging components need to simulate the scenario of using components (changing the incoming value, user interaction, etc.).

When NPM run dev: Components is executed, the developer will see the browser open:

Select the component you want to debug, such as Weex-Dialog, to go to the Weex-Dialog debug screen.

Modify the weeX-Dialog component content in the Packages directory at this time, you will see the modification content in real time, debugging.

The QR code is displayed on the console

In addition, the component we developed is based on WEEX, which means that the developed component needs to support three-terminal (iOS Android H5). Therefore, the TWO-DIMENSIONAL code of js of the current component will be printed in the console for native debugging.

How to output two-dimensional code in console is also a small trick. First, js two-dimensional code library is used to generate two-dimensional code image of resources, and then the mechanism of console output background image is used to print two-dimensional code.

console.log("%c", "padding:75px 80px 75px; line-height:160px; background:url(" + base64 + ") no-repeat; background-size:160px"); Copy the codeCopy the code

The entire debugging page is displayed in a single page. Vue-router is used for routing control. Weex also supports vue-Router, so this single page can run well in Native.

Automatically generates component information

The nav-list.js file is automatically generated each time the NPM run dev: Components command is executed based on the components in the Packages directory. This index file is used to define the routing information for the VUe-Router and the component list for the debug home page. Doing so completely takes the debugging process out of the black box and allows developers to focus only on development in the Packages directory.

const routes = navList.map((item) => { const path = item.path; Return {path, // need vue suffix, otherwise Webpack will require all files under examples with component: require('examples/' + item.exampleRequire + '.vue'), }; }); Routes. Push ({path: '/', Component: require('./app.vue'),}Copy the code
<spon-cell-group> <spon-cell v-for="(page, jndex) in item.list" :key="jndex" :title="page.title" :is-link="true" @click="changePage(page)" ></spon-cell> </spon-cell-group> Copy codeCopy the code

Webpack requires dynamic resources

This article uses WebPack 3.x.x

If the file type is not specified, webpack will require all resources in the directory. The problem is that if there is a certain type of file in the directory and the corresponding loader is not used, an error will be reported during compilation. Without the.vue suffix in the previous section, Webpack will require all resources in the examples directory.

So when defining the component of each route, add the vue suffix and look for the VUE file.

component: require('examples/' + item.exampleRequire + '.vue'), }; Copy the codeCopy the code

Webpack documentation in webpack.js.org/guides/depe…

Require (“./template/” + name + “.ejs”); Webpack parses the require here to get two bits of information:

The directory is./template. The matching rule is /^.*. Ejs $/

Webpack then uses these two pieces of information to get a context Module that contains all modules with the.ejs suffix in the./template directory.

{". / table. Ejs ": 42,". / table - row. Ejs ": 43,". / directory/folder. Ejs ": 44} copy codeCopy the code

There is also a require.context() method that allows you to customize dynamically referenced rules. Examples are in the documentation, and a demo based on this is available on the official website, introducing all the modules in a directory that match the rules.

function importAll (r) { r.keys().forEach(r); } importAll(require.context('.. /components/', true, /.js$/)); Copy the codeCopy the code

Debugging of documents

NPM run dev:docs will open the document debugging server, which is convenient for developers to write documents.

The logic of the document server is located in the Docs directory, again decoupled from the component code. The component information on the left is dynamically taken from the component information in the Packages directory, the component preview on the right uses the component debugging logic directly from the examples directory, and the part in the middle is taken from the readme.md file in the component.

The entire document application is also based on VUE + VUe-Router development.

<div class="nav-bar-container"> <page-nav></page-nav> </div> <div class="document-area-container markdown-body"> <router-view></router-view> </div> <div class="mock-phone-container"> <page-preview :component-name="componentName"></page-preview> </div> Copies the codeCopy the code


is the document content displayed by the corresponding route. When defining the route information, you need to determine the route and the readme.md path corresponding to the route.

const routes = navList.map((item) => { const path = item.path; return { path, component: require('mds/' + item.mdRequire + '.md'), }; }); const router = new VueRouter({ routes, }); Copy the codeCopy the code

Convert markdown vue

The. Md suffix is used when referencing components. Here the loader produced by Ele. me is used. This loader still uses vue-loader to convert the content of MD into HTML first, and then into the single file form required by VUE for vue-Loader.

Var renderVueTemplate = function(HTML, wrapper) {var renderVueTemplate = function(HTML, wrapper) {var $= cheerio.load(HTML, wrapper); { decodeEntities: false, lowerCaseAttributeNames: false, lowerCaseTags: false }); . Result = '<template><${wrapper}>' + $.html() + '</${wrapper}></template>\n' + output.style + '\n' + output.script; return result; }; Copy the codeCopy the code
var result = 'module.exports = require(' + loaderUtils.stringifyRequest( this, '!! vue-loader! ' + markdownCompilerPath + '? raw! ' + filePath + (this.resourceQuery || '') ) + '); '; // Pass the converted string to vue-loader return result; Copy the codeCopy the code

2. Implement engineering based on NPM script

"scripts": { "bootstrap": "npm i", "dev:components": "node build/bin/dev-entry.js", "dev:docs": "node build/bin/docs-dev-entry.js", "build:docs": "node build/bin/docs-build.js", "pub:docs": "npm run bootstrap && npm run clean && node build/bin/release.js", "pub:components": "node build/bin/prepublish.js && lerna publish --skip-npm --skip-git && node build/bin/publish.js", "clean": "Rm -rf docs/dist && rm -rf docs/deploy", "add": "node build/bin/add.js"}, copy the codeCopy the code

In this project, all the commonly used commands are removed, and finally four commands used by the students in the development are exposed:

NPM run dev: debug the components NPM run dev: debug the docs document NPM run pub: publish the docs document NPM run pub: publish the components copy the codeCopy the code

Recommended to see Ruan yifeng blog NPM scripts usage guide, will be a very detailed introduction to the NPM scripts.

Automatic scaffolding generation

NPM run Add automatically adds scaffolding information required by a component to facilitate the developer to add new components.

The jSON-templater/String module is recommended for handling string templates.

Some values in the scaffold file will be different according to the component name, and the corresponding scaffold content will be automatically generated according to the component name, which is more convenient for development.

npm link

The components are developed locally, the examples and documentation are all written, but it’s not clear if there will be any weird bugs if you use them in a real project.

The most primitive approach is to copy components into the NPM package in your project for real debugging.

Of course, NPM also provides a way to solve this problem.

1. First execute the NPM link in the root directory of the SPon-UI component library

2, back to the project directory, execute NPM link spon-ui, two commands will map the project reference spon-UI to the local spon-ui directory.

3. NPM unlink cancels the soft link.

3. Source code dependency

The NPM script mentioned in the previous section does not mention the process of packaging components, because packaging at the component level would add some redundant WebPack code and extra bytes, and the component library is now entirely for internal project use. The packaging environment exists in the project, so there is no need to package in advance.

So distributed component packages are all components under packages, projects are dependent on component source code, called source code dependencies.

To achieve source dependency, you need to modify the configuration of Babel in the business project (not the component project). Exclude spon-UI components

module: { rules: [ { test: /.js$/, exclude: /node_modules(?!/.*(spon-ui).*)/, loader: 'babel-loader', options: {cacheDirectory: true,},},],}, copy codeCopy the code

Didi has a webPack application compilation and optimization path that talks about the benefits of source code dependency.

4. Publish components

We used LerNA to manage the release of components. Lerna can be published in two ways, one is all the components of a project as a release package, and one is to distribute multiple components in a project separately.

We use the first, where all components are unified into a distribution. It’s not as powerful as Lerna, but it’s good for pre-release version number management. In the future, if you want to release individual components separately, change the configuration.

Version management

Test version management

As mentioned earlier, the development of component libraries still depends on the iteration of requirements. There is no one in a small team to develop components. The development of components will follow the iteration of requirements.

After the initial component change requirements are reviewed, the project will formally enter the development process. Project development distinguishes between test environment and pre-release full environment, so component version numbers need to distinguish between test environment and full environment.

npm publish --tag

NPM publish automatically sets the tag “latest” every time it publishes, so we need to add a new tag

npm publish --tag dev

This command adds a tag named dev and labels the release number as dev.

Run the NPM dist-tag ls spon-ui command to view the version of the current label.

➜ spon-ui git:(master) NPM dist-tag ls spon-ui dev: 0.1.0-12 Latest: 0.1.0Copy the code

When installing SPon-UI in a project, do it individually

NPM I spon-ui@dev NPM I spon-ui@latest Copy the codeCopy the code

5, npm5 package – lock. Json

Once the component was released and ready for use in the project, we updated from NPM3.x to NPM5, but found that the implementation of NPM I was not consistent with the popular science text on the web.

No matter how you modify the package.json file and repeat NPM I, NPM will download it according to the version information described in the Lock file.

In the case of repeating NPM I, the NPM will download the update module based on the Semantic versioning version of the package in package.json, regardless of the lock information.

A review of the information shows that the rules for NPM I have changed three times since the release of NPM 5.0.

1, NPM 5.0.x, no matter how package.json changes, NPM I will be based on lock file download

Github.com/npm/npm/iss… This issue complains about this problem. Obviously, package.json has been manually changed, why don’t you give me an upgrade package? This then led to problems with 5.1.0…

2. After 5.1.0, NPM install will ignore the lock file to download the latest NPM

Then someone brought up the issue github.com/npm/npm/iss… Complaining about this problem eventually evolved into rules after version 5.4.2.

3. After 5.4.2 github.com/npm/npm/iss…

If package.json is changed and the package.json file is different from the lock file, then NPM I will download the latest package according to the version number and semantic meaning of the package and update it to lock.

If the two are in the same state, then NPM I is downloaded according to lock, regardless of whether the actual package version is new.

conclusion

The above is our practice of migrating UI components from the project to be maintained in the form of a separate NPM package. It is not perfect yet, and I hope that some of the content can help you.