This article roughly summarizes the understanding of the front-end view library/framework; This paper summarizes the use skills of popular progressive framework Vue and looks into the future.
1 Front-end evolution process
1.1 The front and back ends are not separated without Ajax
Before Ajax, there was no “front end.” The amount of HTML and CSS code in the project is small, and the logical complexity of javaScript processing is not high. So string template engines like Java and JSP were the order of the day.
1.2 Bottlenecks and split
In 1998, with the advent of Ajax and the continuous improvement of user experience requirements, the entropy of view layer increased, MVC model also became the best practice, and the separation of front and back ends became a trend.
1.3 Standardization, modularization, componentization and automation
In 2006, in the midst of the browser wars, developers were struggling with compatibility and cumbersome DOM manipulation, and the popularity of jQuery greatly solved this problem.
In 2008, the front-end single file thousands of lines, the dependency relationship chaos; Hence the concept of front-end modularity. Common. Js, AMD, CMD specifications followed the modular approach to ES6.
The essence of componentization is to model the minimum material of the design draft. At the software level, a component might be a string template or a VNode.
The essence of automation is to reduce human involvement through technology in workflows, packaging processes, and development specifications.
1.4 engineering
Software engineering is concerned with performance, stability, availability, maintainability and other aspects, all to these as the goal of the work is “front-end engineering”.
The modularization, componentization and automation schemes mentioned above are all “techniques”.
1.5 creativity
Creativity = professional skills + thinking skills + task content driven
1.6 summarize
There is a process of people working together under the organizational structure, and the business brings with it tasks, expertise do we have a body of knowledge with breadth and depth? And can you retrieve the output quickly?
Structured tasks = Task content driven + expertise
Histories make men wise; poets witty; the mathematics subtle; natural philosophy deep; moral grave; logic able to contend. Where there is learning, is a character.
Therefore, only by learning knowledge in different fields and sticking to existing systems can creativity be realized.
Creativity = professional skills + thinking skills + task content driven
2. Reserve knowledge
2.1 Product life cycle
Generally speaking, the product life cycle includes business landing period, technical debt period, and requirements iteration period.
2.2 SPA Architecture in jQuery
Architecture scheme :MVC separation and SPA
Technical selection :jQuery, easyUI, director.js(routing scheme)
Think about how a project is organized like 🤔, We’ll get more exposure to javaScript, DOM API (get selectors, get dimensions), BOM, event mechanisms in browsers (bubble, capture, event objects), XHR objects, javaScript object-oriented (ES5’s parasitic composite, ES6’s class), microtasks, macro tasks, and other classic book concepts. With the advent of front-end frameworks, data-driven views and MVVM concepts, part of the practice has been business precipitation, and the frequency of contact in the development process has started to become less and less, replaced by the capabilities of the framework.
/index.html <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, <meta http-equiv=" x-UA-compatible "content=" IE =edge"> <title>Document</title> href="./stylesheets/myStyle.css"> <link rel="stylesheet" type="text/css" href="./stylesheets/themes/default/easyui.css"> <link rel="stylesheet" type="text/css" href="./stylesheets/themes/icon.css"> </head> <body> <div id="root"></div> <script type="text/javascript" SRC = "/ javascripts/jquery - 1.12.4. Min. Js" > < / script > < script type = "text/javascript" src="./javascripts/jquery.easyui.min.js"></script> <script src="./javascripts/director.js"></script> <script src="./javascripts/jquery.ui.widget.js"></script> <script src="./javascripts/jquery.fileupload.js"></script> <script type="module" src="./app.js"></script></body> </html> /app.js import Login from "./modules/Login.js"; $.ajaxSettings.beforeSend = function (xhr, request) { const user_token = window.localStorage.getItem('user_token'); SetRequestHeader ('Authorization', 'Bearer ${user_token}'); } const routes = {"/login": () => {new login ("#root")}} const router = router (router). Configure ({recurse: recurse) "forward" }); Router.init (); location.hash = location.hash || '/login'; /Base.js export default class Base { constructor(el) { this.el = el; // the root node of the SPA promise.resolve ().then(() => this.init()); } init() {this.render(); $.parser.parse(); EasyUI this. Mounting (); this.handle(); } render() {} mounting() {} handle() {}} /modules/ login. js Import Base from "./ base.js "; Import admini from "... /services/login.js"; Export default class Login extends Base {constructor(el) {super(el); this.admini = { name: "", password: "", }; }, Render () {const template = '<table id="login"> <tr><td> <td><input class="name" type="text"></td></tr> < tr > < td > administrator password < / td > < td > < input class = "password" type = "password" > < / td > < / tr > < tr > < td > < a class = "loginBtn" Href = "#" > login < / a > < / td > < td > < a class = "regBtn" href = "#" > register < / a > < / td > < / tr > < / table > `; $(this.el).html(template); }, async AdminiLogin(){ const data = await admini.AdminiLogin(this.admini); }, handle(){ $(".name").textbox({ onChange: (newVal) => (this.admini.name = newVal), iconCls: "icon-man", iconAlign: "right", }); const passwordbox = $(".password").passwordbox({ onChange: (newVal) => (this.admini.password = newVal), showEye: true, iconAlign: "right", }); $(".loginBtn") .linkbutton() .on("click", (e) => { e.preventDefault(); this.AdminiLogin(); }); $(".regBtn") .linkbutton() .on("click", (e) => { e.preventDefault(); location.hash = "/reg"; }); }Copy the code
Description:
When using jQuery and template engine to realize SPA architecture, the routing scheme is director.js, refer to director.js
The template engine renders fully with innerHTML($.html() in jQuery), which is inefficient
Data changes, you need to manually add onChange event for listening; It’s basically a hard-coded notification mechanism between objects
Event bindings need to be added repeatedly to prevent event bubbling, string trim, and so on
Thinking 🤔 Mock data optimized the front-end process, promote the parallel development, webpack build tools such as optimization of a series of manual packaging (such as compression, devServer) and development experience, the front-end framework to solve the problem in addition to the above development level (that is, all the so-called wheels calls) problem, which provides support in other ways?
2.3 Design Mode
Publish the subscriber model
The publisk-subscribe pattern, also known as the observer pattern, defines a one-to-many dependency. When a state changes, all objects in that state are notified.
1. Publish and subscribe can be widely used in asynchronous programming.
2. Publish subscribe can replace hard-coded notification between objects.
Application of publish subscriber pattern to Vue responsive system, see from Publish – subscribe pattern to Vue responsive System
Adapter mode
What is cross-end rendering? In fact, the “end” here is not limited to the traditional PC side and mobile side, but the abstract Renderer. The render layer is not limited to browser DOM and native UI controls on mobile devices, it can be used in static files and even virtual > real environments.
Application of adapter mode in cross-terminal, refer to brief analysis of React/Vue cross-terminal rendering principle and implementation
2.4 Virtual DOM and DIff algorithm
If we want to implement a virtual DOM ourselves, there should be three steps:
-
Compile, how to compile the real DOM into vNodes.
-
Diff, how do we know what’s changed between oldVnode and newVnode?
-
Patch, if these changes are patched to the real DOM.
Refer to snabbdom.js source code, we know:
There is a very important function called sameVnode, which is very important and has a huge impact on the diff algorithm. In general, the time complexity of finding the smallest change between two arbitrary trees is O(n^3), which is not acceptable. Fortunately, we can make the following assumptions about the Virtual DOM tree: If oldVnode and VNode are different (e.g., type changes from div to P, or key changes), meaning that the entire Vnode is replaced (because we don’t normally move vNodes across layers), There is no need to compare vNodes’ children. Based on this assumption, we can decompose the tree by hierarchy, which greatly simplifies the complexity to near O(n)
The source code for the sameVnode method is as follows:
Function sameVnode(vnode1: VNode, vnode2: VNode): function sameVnode(vnode1: VNode, vnode2: VNode): boolean { return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel; }Copy the code
For the application of Virtual DOM and snabbdom.js, see Virtual DOM and snabbdom.js
3. Practice of componentization in Vue
3.1 The nature of components
The essence of a component is a granular collection of visual elements. Component ideas are usually implemented, including string template engines and vNodes based on the virtual DOM
Description:
String template engine related technologies include JSP, freemarker, Handlebars. Js, ETpl. The template engine is strictly defined. Enter the template string + data to get the rendered string, and use innerHTML for the full replacement.
The virtual DOM is re-rendered into the entire AST on each render, using diff + patch on the client side and serialize as a string on the server side.
Static content, direct server rendering, fast first screen speed. Dynamic interfaces, on the other hand, should be done in the same way as application architecture (MV*, component tree)
For a discussion of front-end frameworks and template engines, what are some useful front-end template engines?
3.2 Basic use of Components
Refer to the business logic module implementation code of SPA architecture in 2.2. JQuery above
Instructions, filters, modifiers, slots
If we don’t have v-if, V-for,.stop,.number, etc., in the code above, we need to repeatedly create variables for the condition, and keep calling the event preventDefault method to perform type-casting
Refer to common modifiers in VUE and write a long-press instruction using VUE
Prop, data, computational properties, observation properties, lifecycle, components
Vue’s responsive system (see the published subscriber pattern above) is the solution to the hard-coded communication mechanism between code objects described above. Prop and Data, respectively, external and internal data. Computed provides many-to-one and Watch provides a one-to-many response. And life cycle, in addition to the official figure, it is worth mentioning use $on calls the use of the hook, and the use of Vue. Config. OptionMergeStrategies merge strategy customization we need life cycle
Reference summary of the VUE knowledge system of practical tips and these VUE skills, you are worth having, as well as ahead of high energy, this is the latest wave of VUE combat skills, once used, a surprising use
3.3 Component Communication
1. Parent-child communication: V-bound &props and V-on & EMIT; This. Refs. RefName and parent; Slote and the parent; Slote and the parent; Slote and scopedSlots 2. Cross-level communication :provide and inject; Attr and $Listeners 3. Data management: Vuex, eventBus, Observable 4 Other source communication mechanisms: URL, local storage and session storage, interface request specific implementation refer to the following, no further details.
Check out the 36 Tips you must Know for Vue development and these Vue tips are worth it
4. Practice of routing in Vue
4.1 Routing Table & Mapping
The core concept is to configure the routing table and then dynamically route to match components. Referring to official documents, we can see
-
Configuring a routing table: provides nested routing,
-
Dynamic matching: provides programmatic navigation, named routes, named views, and redirection capabilities
-
Communication: Use prop instead of $route coupling
4.2 Route Guard
Vue-router provides the configuration of load route lifecycle capability and meta information, transition dynamic effect and rolling behavior of route switching, and lazy route loading
It is worth mentioning that the routing table configuration and guard logic are often coupled in engineering, and the following is recommended
/router/index.js import Vue from "vue"; import Router from "vue-router"; import { scrollBehavior } from "./scrollBehavior"; import { createGuard } from "./guard"; import { routes } from "./config"; const router = new Router({ mode: "history", routes, strict: true, scrollBehavior, }); createGuard(router); export default router; Vue.use(Router); /router/guard/index.js import { createScrollGuard } from "./scrollGuard"; import { createProgressGuard } from "./progressGuard"; import { createPermissionGuard } from "./permissionGuard"; export function createGuard(router) { createScrollGuard(router); createProgressGuard(router); createPermissionGuard(router); } / router/apps/createProgressGuard. Js / / other guards related authentication, rolling logic omit the import NProgress from "NProgress"; import "nprogress/nprogress.css"; export function createProgressGuard(router) { router.beforeEach((to, from, next) => { NProgress.start(); next(); }); router.afterEach(() => { NProgress.done(); }); }Copy the code
4.3 Creating dynamic Routes
Vue-router dynamically creates a route, which involves the two apis addRoutes and Macher. The basic process is as follows:
-
Login to obtain token
-
Obtain user information (permission, role, menu) before routing guard
-
Generate the routerMap and set the route (addRoutes)
-
Routing needs to be reset when routing is generated (macher)
-
The menu component obtains the changed routing information and dynamically generates the menu
5. Practice of engineering in Vue
5.1 Automation introduces component & plug-in mechanisms
import Banner from "@/components/Banner"; const plugins = { install(Vue) { Vue.component("Banner", Banner); }}; export default plugins; // Use vue.use in main.js after exportCopy the code
When the number of imported components is large enough, we can use require.context provided by Webpack and regular expression to automatically obtain the components in the file for registration, so the specification of the folder is very important
import Vue from 'vue'; const componentsContext = require.context('./', true, /index.(vue|js)$/); // require.context takes three parameters: the file path to find, whether to find subdirectories, To match the file's re componentSContext.keys ().foreach ((fileName) => {// Get the file in the default module const componentConfig = componentsContext(fileName).default; if (/.vue$/.test(fileName)) { Vue.component(componentConfig.name, componentConfig); } else { Vue.use(componentConfig); }});Copy the code
See require.context and vuex persistence
In the same way, we can optimize in VUex
import Vue from 'vue';
import Vuex from 'vuex';
import getters from './getters';
Vue.use(Vuex);
const modulesFiles = require.context('./modules', true, /\.js$/);
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default r
eturn modules
}, {})
const store = new Vuex.Store({ modules, getters})
export default store
Copy the code
5.2 Script Creation Components
Exports = {vueTemplate: // module.exports = {vueTemplate: CompoenntName => {return '<template> <div class="${compoenntName}"> ${compoenntName} component </div> </template> <script> export default { name: '${compoenntName}' } </script> <style lang="scss" scoped> .${compoenntName} {} </style>` }, entryTemplate: 'import Main from './main.vue' export default Main'} // Generatecomponent.js Creates file const chalk = using node.js fs module and command line require('chalk') const path = require('path') const fs = require('fs') const resolve = (... file) => path.resolve(__dirname, ... file) const log = message => console.log(chalk.green(`${message}`)) const successLog = message => console.log(chalk.blue(`${message}`)) const errorLog = error => console.log(chalk.red(`${error}`)) const { vueTemplate, entryTemplate } = require('./template') const generateFile = (path, Data) => {if (fs.existssync (path)) {errorLog(' ${path} file already exists') return} return new Promise((resolve, resolve, reject) => { fs.writeFile(path, data, 'utf8', Err => {if (err) {errorLog(err.message) reject(err)} else {resolve(true)}})})} log(' Please enter the name of the component to be generated, if you want to generate global components, Let componentName = "process.stdin.on('data', async chunk => { const inputName = String(chunk).trim().toString() const componentDirectory = resolve('.. /src/components', inputName) const componentVueName = resolve(componentDirectory, 'main.vue') const entryComponentName = resolve(componentDirectory, 'index.js') const hasComponentDirectory = fs.existsSync(componentDirectory) if (hasComponentDirectory) { ErrorLog (' ${inputName} component directory already exists, Return} else {log(' generating component directory ${componentDirectory} ') await dotExistDirectoryCreate(componentDirectory) } try { if (inputName.includes('/')) { const inputArr = inputName.split('/') componentName = inputArr[inputArr.length - } else {componentName = inputName} log(${componentVueName} ') await generateFile(componentVueName, VueTemplate (componentName)) log(' generating entry file ${entryComponentName} ') await generateFile(entryComponentName, EntryTemplate) successLog(' generated successfully ')} Catch (e) {errorLog(e.message)} process.stdin.emit('end')}) process.stdin.on('end', () => { log('exit') process.exit()} ) function dotExistDirectoryCreate (directory) { return new Promise((resolve) => { Mkdirs (directory, function () {resolve(true)})} callback) { var exists = fs.existsSync(directory) if (exists) { callback() } else { mkdirs(path.dirname(directory), function () { fs.mkdirSync(directory) callback() }) }}Copy the code
Refer to vuE-CLI3 project from construction optimization to Docker deployment
5.3 Component Abstraction Layer
In the CSS architecture methodology, BEM talks about thinking of views as blocks, elements, modifiers.
In nature, matter is ever-changing, and matter is composed of organic and inorganic matter, and both belong to compounds. In the process of learning chemistry, we will not pay attention to all compounds, but the properties and general laws of those common typical compounds.
By analogy, we can see that views are like substances and components are like compounds. We take common, typical components from the business and separate them into common level components, deducing other business level components from common level components. Javascript, CSS, and HTML, which are raw materials, are like smaller atomic features that are lower level buildings than components.
5.4 Unified management of state & CSS variables
Centrally manages CSS variables in the Store and mounts inline styles in the container
It is worth mentioning that CSS variables are officially supported in Vue3.0
/store/index.js ... state: { styleVariable: { "--mainColor": "#ff9141", "--fontSize": "16px", "--transition": "Cubic bezier - 0.3 (0.165, 0.84, 0.44, 1) s", "- transitionEasyOut" : "0.6 s all ease - out",}}... / app. vue <div style={this.styleVariable}> <router-view></router-view> </div> / var(--mainColor); transition: var(--transition);Copy the code
5.5 mixins
What problem was solved?
Reuse of logic
Disadvantages:
The template data source is not clear
Namespace conflict
Mixins export default {data() {return {fadeInElements: [], // possible namespace conflicts}; }, methods: {handleScroll() {// possible namespace conflicts for (var I = 0; i < this.fadeInElements.length; i++) { var elem = this.fadeInElements[i]; if (this.isElemVisible(elem)) { elem.style.opacity = "1"; elem.style.transform = "scale(1)"; this.fadeInElements.splice(i, 1); } } }, isElemVisible(el) { var rect = el.getBoundingClientRect(); var elemTop = rect.top + 50; var elemBottom = rect.bottom; return elemTop < window.innerHeight && elemBottom >= 0; }, }, mounted() { this.fadeInElements = Array.from( document.getElementsByClassName("fade-in") ); document.addEventListener("scroll", this.handleScroll); this.$on("hook:beforeDestroyed", () => { document.removeEventListener("scroll", this.handleScroll); }); this.handleScroll(); }}; <script> import scollTransition from "@/mixins/scollTransition"; Mixins: [scollTransition]}; mixins: [scollTransition]}; </script>Copy the code
5.6 JSX&HOC
What problem was solved?
Reuse of logic
Disadvantages:
The template data source is not clear
Namespace conflict
No performance overhead
/App.js <script> import { withLog, withNetwork, withPageChange, compose,} from "@/utils/highOrderFunc"; const view = { render() { return ( <div> <router-view></router-view> </div> ); }}; export default compose(withLog, withNetwork, withPageChange)(view); </script> <style lang="less"></style> /utils/highOrderFunc. funcs) => funcs.reduce((a, b) => (... args) => a(b(... args))); $listeners => {return {on: vm.$listeners, attr: vm.$attrs, scopedSlots: vm.$scopedSlots, }; }; @wrapped {*} wrapped */ const withLog = (wrapped) => {return {mounted() { }, render(h) {return h(wrapped, normalizeProps(this)); }}; }; @wrapped {*} wrapped */ const withNetwork = (wrapped) => {return {mounted() {} wrapped */ const withNetwork = (wrapped) => {return {mounted() { window.addEventListener("online", this.onlineHandler); window.addEventListener("offline", this.offlineHandler); this.$on("hook:beforeDestroyed", () => { document.removeEventListener("online", this.onlineHandler); }); this.$on("hook:beforeDestroyed", () => { document.removeEventListener("offline", this.offlineHandler); }); }, methods: {onlineHandler() {this.$message({showClose: true, message: "network normal ", type: "success",}); }, this.$message({showClose: true, message: "network error", type: "error",}); }, }, render(h) { return h(wrapped, normalizeProps(this)); }}; }; Wrapped */ const withPageChange = (wrapped) => {return {// Use the lifecycle of Vue's wrapped model described above pageVisible() { console.log("pageVisible"); }, pageHidden() { console.log("pageHidden"); }, render(h) { return h(wrapped, normalizeProps(this)); }}; }; export { compose, normalizeProps, withLog, withPageChange, withPromise,};Copy the code
See HOC for advanced Vue components
5.7 hooks
Vue3’s composition-API gives you the ability to use hooks in Vue; Vuex and VUE-Router are also reconstructed
What problem was solved?
Reuse of logic
Problems with Minxis, HOC, render-props
import { defineComponent } from 'vue'
import { useRoute, useRouter } from 'vue-router'
export default defineComponent(_ => {
const route = useRoute()
const router = useRouter()
console.log(route.params.id)
router.push('/xxx/xxx')
})
Copy the code
See the new vue-router hooks usage
5.8 Operations related to deployment
The Vue project was originally packaged and deployed in this way
In addition to deploying front-end projects to the cloud, how do you deploy nodeJS projects and mongoDB to the server? How do I record logs and monitor exceptions? How to use Redis for caching? How to optimize workflow through CI/CD? How to optimize the deployment process with Docker? How to do gray publishing and automatic rollback?
6. Summary
-
Technological evolution = engineering + creativity;
Engineering is the process of systematization and ecologicalization of things with increasing disorder entropy.
Creativity = professional skills + thinking skills + task content driven;
-
A SPA architecture application based on jQuery + easyUI + director.js(routing) is simply described. The differences between full rendering based on the string template and incremental rendering based on the virtual DOM are compared. Also mentioned how the virtual DOM facilitates automated packaging of build tools (Webpack, rollup), isomorphism of front and back ends, cross-end technology
-
A brief introduction to the basic concepts of VUE, as well as an understanding of components, routing and communication
-
How to optimize code in the vUE project in the form of best practices; Plug-ins, componentalization, automatic file generation, CSS and JS communication, code reuse schemes are mentioned
-
The article focuses on development thinking, with some mention of deployment.