sequence

The concept of the big front end has been mentioned again and again this year, so what is the big front end era? The word “big front end” was originally derived from the fact that there were many front-end developers in Ali who wrote both front-end and Java Velocity templates. However, the scope of big front end has been increasingly expanded, including front-end + mobile end. Front-end, CDN, Nginx, Node, Hybrid, Weex, React Native, and Native apps. The author is an ordinary full-time iOS developer. After getting into front end development, I found that the front end has something to learn from the mobile end. Therefore, I created this series of articles about the Big Front End Era, hoping that the two can learn from each other’s excellent ideas. When it comes to big front ends, topics that are often mentioned are: componentization, routing and decoupling, engineering (packaging tools, scaffolding, package management tools), MVC and MVVM architectures, burying points and performance monitoring. The author starts from the aspect of componentization. There are a lot of articles on the web about front-end framework comparisons (React, Vue, Angular), but not many cross-end comparisons. The author is going to compare the former end and mobile end (mainly iOS platform) to see the different practices of the two ends, and discuss whether there is any mutual learning.

The front part of this article may be the front god will feel more basic, if there is a mistake, please give advice.


Vue article

I. The need for componentization

In order to improve code reuse and reduce repetitive development, we split the relevant code according to template, style and script and encapsulated it into components one by one. Components extend HTML elements and encapsulate reusable HTML code, and we can think of components as custom HTML elements. In Vue, each packaged component can be viewed as a ViewModel.

How to encapsulate components

When it comes to packaging, let’s talk about how components are organized.

In a simple SPA project, Vue.com Ponent can be used to define a global component, but once the project is complex, there will be drawbacks:

  1. Global definitions enforce that names in each component must not be repeated
  2. String templates lack syntax highlighting and use ugly \ when HTML has multiple lines
  3. No CSS support (No CSS support) means that CSS is conspicuously left out when HTML and JavaScript are componentized
  4. No build step restricts the use of HTML and ES5 JavaScript to preprocessors such as Pug (formerly Jade) and Babel

In addition, most corporate projects will introduce engineering management, using package management tools, such as NPM or YARN. So the way Vue defines a component in a complex project with Vue.com Ponent is not appropriate. Single-file components are needed here, and you can also use build tools like Webpack or Browserify. For example, with the hello. vue component below, the entire file is a component.

In a single file component, the entire file is a CommonJS module, which contains the corresponding HTML of the component, the processing logic of the component Javascript, and the style CSS of the component.

In the component’s Script tag, you need to encapsulate the behavior of the component’s ViewModel.

  • Data component initialization data, as well as private properties.
  • Properties of the props component, which is used specifically to receive data that the parent component communicates with. (This is analogous to @Property in iOS.)
  • The processing logic functions within the Methods component.
  • Watch requires additional listening properties (analogous to KVO in iOS)
  • Computed properties of a computed component

  • The child components used by Components

  • Lifecycle hooks Hook functions for the lifecycle. A component also has a life cycle, including the following: Life cycles such as beforeCreate, created, beforeMount, Mounted, beforeUpdate, updated, activated, deactivated, beforeDestroy, and destroyed. We can add our default processing logic to these hook functions. (This is analogous to the ViewController lifecycle in iOS.)

In this way, encapsulating a single file component in Vue is exactly the same idea as encapsulating a ViewModel in iOS. The rest of the discussion, without exception, deals with single-file components.

How to divide components

General partition components can be divided according to the following criteria:

  1. Page area: Header, footer, sidebar…
  2. Function modules: Select, Pagenation……

Here’s an example of how a front-end divides components.

1. Page area

Again, take objC China’s home page as an example

We can abstract the top page according to the layout, first the middle of the image, then divide the components by the area of the page, finally the right side of the component tree.

In the root component of the Vue instance, load the Layout.


import Vue from 'vue';
import store from './store';
import router from './router';
import Layout from './components/layout';

new Vue({
  el: '#app',
  router,
  store,
  template: '<Layout/>'.components: {
    Layout
  }
});Copy the code

Based on the abstract component tree, you can further subdivide each component down.

The next layer of layout consists of header, footer, and Content, which constitute the entire layout. Vue single file component.

This is the complete implementation of our layout.vue. NavigationBar, footerView, and Content are referenced within this single file component. Since the content consists of routing pages, it is declared as router-view.

NavigationBar. Vue, footerView, layout.vue

2. Function modules

In general projects, the details page has the most content. Let’s take objC China’s details page as an example

On the left is the detail page and on the right is the functional breakdown. We have divided the page into six sub-components.

Expand from top to bottom, as shown above.

After the division of the function, the code of the whole detail page becomes extremely clean, the whole page is 6 single file sub-components, the logic of each sub-component is encapsulated in their respective components, the detail page is to assemble them together, the code is highly readable, the later maintenance is also very convenient.

Details page specific code here github.com/halfrost/vu…

The code for the 6 sub-components is here github.com/halfrost/vu… , the specific code see the link, here will not be repeated.

To sum up, the front-end SPA page is abstracted into a large component tree.

Principle of componentization

Here’s an example:

<! DOCTYPE html> <html> <body> <div id="app"> <parent-component> </parent-component> </div> </body> <script src="js/vue.js"></script> <script> var Child = Vue.extend({ template: '<p>This is a child component ! </p>'}) var Parent = vue.extend ({// use the <child-component> tag template :'<p>This is a Parent component! </p><child-component></child-component>', components: {// Locally register the child component, which can only use 'child-component' within the Parent component: Vue.component('parent-component', Parent) new Vue({el: '#app'}) </script> </ HTML >Copy the code

In the example above, we declare a

inside the parent and render the result as follows:


This is a Parent component !
This is a child component !Copy the code

The above code is executed in the following order:

  1. Child components are first registered with components in the parent component.
  2. The parent component is registered globally with Vue.com Ponent.
  3. When rendering the parent component, render to<child-component>Renders the child components as well.

It’s worth noting that Vue follows the following common HTML restrictions when parsing templates:

  • A cannot contain other interactive elements (such as buttons, links)
  • Ul and OL can only contain Li directly
  • Select can contain only Option and OptGroup
  • Table can only contain thead, tbody, tfoot, TR, Caption, Col, colgroup directly
  • Tr can only contain th and TD directly

5. Component classification

The types of components can be divided into the following four categories:

  1. Common components
  2. Dynamic components
  3. Asynchronous components
  4. Recursive components

1. Common components

I won’t go into the general components here.

2. Dynamic components

Dynamic components take advantage of the is feature, which allows multiple components to use the same mount point and switch dynamically.


var vm = new Vue({
  el: '#example'.data: {
    currentView: 'home'
  },
  components: {
    home: { / *... * / },
    posts: { / *... * / },
    archive: { / *... * / }
  }
})


<component v-bind:is="currentView">
  <! Component changes when Vm. currentView changes! -->
</component>Copy the code

Now that the type of the < Component > component is currentView, we can dynamically load components by changing the currentView value. In the above example, currentView in data can be changed continuously to dynamically load the home, posts, and Archive components.

3. Asynchronous components

Vue allows you to define a component as a factory function that is triggered to dynamically parse the component when it needs to be rendered and cache the results:


Vue.component("async-component".function(resolve, reject){
    // async operation
    setTimeout(function() {
        resolve({
            template: '<div>something async</div>'
        });
    },1000);
});Copy the code

Dynamic components can be used in conjunction with WebPack to achieve code segmentation, which can be divided into blocks and downloaded using Ajax when needed:


Vue.component('async-webpack-example'.function(resolve) {
  // This particular require syntax tells Webpack
  // Automatically split compiled code into different blocks,
  // These blocks will be downloaded automatically through ajax requests.
  require(['./my-async-component'], resolve)
});Copy the code

4. Recursive components

If a component has the name attribute set, it can become a recursive component.

A recursive component can recursively call itself repeatedly using the name in the template.


name: 'recursion-component'.template: '<div><recursion-component></recursion-component></div>'Copy the code

The above code is an error code that will cause a recursive loop when writing templates, and will eventually report “Max Stack size exceeded”. Solutions need to break the loop, such as v-if returning false.

Message passing and state management between components

In Vue, component messages are delivered in three main ways:

  1. Message passing between parent and child components
  2. Event Bus
  3. Vuex unidirectional data flow

1. Message passing between parent and child components

The transmission mode of parent and child components is relatively simple. After Vue 2.0, the relationship between parent and child components can be summarized as props down and events Up. The parent component passes data down to the child component through props, and the child component sends messages to the parent component through Events.

Father passes to son

Here’s an example:


Vue.component('child', {
  / / declare the props
  props: ['msg'].// Prop can be used in templates
  // Can be set with 'this. MSG'
  template: '<span>{{ msg }}</span>'
})

<child msg="hello!"></child>Copy the code

A MSG property is declared in the props of the Child component, which is used in the parent component to pass the value to the child component.

One thing to note here is that in non-string templates, the camelCased (hump) named prop needs to be transformed to the corresponding Kebab-case (short strike-separated) name.

Vue supports dynamic binding as well. Vue also supports dynamic binding functions with the V-bind directive.

While parent to child is a one-way data flow, PROP is one-way bound: when a property of the parent changes, it is passed to the child, but not vice versa. This is to prevent the child from inadvertently modifying the parent’s state — which can make the application’s data flow difficult to understand.

In addition, every time the parent component is updated, all prop of the child component is updated to the latest value. This means that you should not change prop inside child components. Vue recommends that the props of the subcomponent be immutable.

There are two types of problems involved:

  1. Due to unidirectional data flow, the data or state of the child component is inconsistent with that of the parent component. In order to synchronize, the data or data of the parent component is modified by reversing the data flow in the child component.

  2. When a subcomponent receives the props value, it wants to change it for two reasons. First, when a prop is passed as an initial value, the subcomponent wants to use it as local data. The second reason is that prop is passed in as an initial value and processed by the child component into other data output.

Both types of problems, enforced by developers, are also achievable, but lead to unsatisfactory “consequences”. The first problem forces manual changes to the data or state of the parent component, resulting in chaotic data flow. It is difficult to understand the state of the parent component just by looking at it. Because it can be modified by any child component! Ideally, only the component itself can modify its state. After the second problem forces the props of the child component to be manually modified, Vue will issue a warning on the console.

What if we could solve these two problems gracefully? One by one:

(1) The first problem can be solved by bidirectional binding.

In Vue versions later than 2.3.0+, there are two ways of bidirectional binding

The first way:

Use the.sync modifier as a compile-time syntactic sugar after Vue 2.3.0+. It is extended to a V-ON listener that automatically updates the properties of the parent component.


// Declare a bidirectional binding
<comp :foo.sync="bar"></comp>


// The above line of code will be extended to the following line:
<comp :foo="bar" @update:foo="val => bar = val"></comp>

// When a child component needs to update the value of foo, it explicitly fires an update event:
this.$emit('update:foo', newValue)Copy the code

The second way:

Custom events can be used to create custom form input components that use v-Models for two-way data binding.


<input :value="value" @input="updateValue($event.target.value)" >Copy the code

Bidirectional binding in this manner must satisfy two conditions:

  • Accept a value attribute
  • The input event is emitted when there is a new value

The two officially recommended ways of bidirectional binding are the above two methods. However, there are also some implicit bi-directional bindings that can inadvertently cause bugs.

The parent component passes data to the child component. In particular, if the data passed is of reference type (such as array and object), then the default is two-way data binding. Changes to the child component will affect the parent component. In this case, some unintelligible bugs can occur if you don’t know, so you need to be aware of reference-type data passing.

(2) For the second question, there are two approaches:

  • The first is to define a local variable and initialize it with the value of prop:

props: ['initialCounter'].data: function () {
  return { counter: this.initialCounter }
}Copy the code
  • The second approach is to define a calculated property that handles the value of prop and returns it.

props: ['size'].computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}Copy the code

Parent to child can also pass templates, using slot to distribute content.

Slot is a custom element directive built into Vue. Slot The bind callback retrieves the element to be replaced by name. If there is something in the context that needs to be replaced, the replaceChild method on the parent element is called to replace the slot element with the replacement element. Otherwise, delete the element to be replaced. If there is a top-level element in the replacement slot element, and the first child of the top-level element is a DOM element, and the node has a V-if instruction, and the slot element has content, the replacement template increments what the V-else template puts into the slot. If the V-if directive is false, else template content is rendered.

Child to parent

Child components pass data back to parent components in a single way, using custom events!

The parent component listens for events using $ON (eventName) and the child component fires events using $emit(eventName)

Here’s a simple example:

<button @click="emitMyEvent">emit</button> emitMyEvent() {this.$emit('my-event', this.hello); <child @my-event="getMyEvent"></child> getMyEvent() {console.log(' I got child event '); }Copy the code

This.$parent or this.$children calls the parent and/or child methods. So in this array you can just grab all the VC’s, and then you can call the methods that are exposed in dot h. But this approach is too directly coupled to each other.

2. Event Bus

The concept of Event Bus is also familiar to students on mobile terminals, because it has been used in Android development. In iOS development, the analogy is a message bus. The implementation can be signalling in Notification or ReactiveCocoa.

The Event Bus is also implemented using Vue instances. Create a new Vue dedicated to the message bus.


var eventBus = new Vue()

// Add eventBus to component A
eventBus.$emit('myEvent'.1)

// Listen on the component you want to listen on
eventBus.$on('id-selected', () = > {// ...
})Copy the code

3. Vuex unidirectional data flow

Since this article focuses on componentization, Vuex only explains usage here, and the principle will be analyzed in a separate article later.

This is a picture of what Vuex is. Vuex state management mode developed specifically for vuue.js applications. It uses centralized storage to manage the state of all components of an application and rules to ensure that the state changes in a predictable way.

The direction of the arrow in the figure above describes the flow of data. The flow of data is one-way, from Actions to State, where the data in the State changes and affects how the View displays the data.

From simple Actions, State, View three roles, to now add a Mutations. Mutations now turns out that the only way to change the state in Vuex’s store is to submit mutation. Mutations in Vuex are very similar to events: each mutation has a string event type (type) and a callback function (handler).

The Mutation method is typically called by commit in the component


this.$store.commit('increment', payload);Copy the code

The difference between Actions and Mutations lies in:

  • The Action commits mutation rather than a direct state change.
  • Action can include any asynchronous operation, while Mutations must be a synchronous function.

Typically, dispatch calls the Actions method in the component


this.$store.dispatch('increment');Copy the code

According to the best practices of Vuex, the official Vuex provides a project template structure. We hope that everyone can organize our projects according to this model.

├─ index.html ├─ download.js ├─ API │ ├─ ├─ ├─ # extract API request ├ ─ ─ components │ ├ ─ ─ App. Vue │ └ ─ ─... └ ─ ─ store ├ ─ ─ index. Js # we assembled module and export store ├ ─ ─ actions. Js # root level action ├ ─ ─ mutations. Js # root level mutation └ ─ ─ modules ├─ ├─ class.js #Copy the code

The detailed code for this example is here

Vii. Component registration method

There are two main ways to register components: global registration and local registration

1. Global registration

Global registration using the Vue.component directive


Vue.component('my-component', {
  / / options
})Copy the code

The registered component can then be used in the parent instance as a custom element < my-Component >
.


/ / register
Vue.component('my-component', {
  template: '
      
A custom component!
'
}) // Create the root instance new Vue({ el: '#example' }) <div id="example"> <my-component></my-component> </div>Copy the code

2. Partial registration

Registering components globally can slow down page loading, and some components only need to be loaded as needed, so you don’t need to register every component globally. So there’s a local registration.



var Child = {
  template: '
      
A custom component!
'
} new Vue({ // ... components: { // will only be available in the parent template 'my-component': Child } })Copy the code

IOS article

I. The need for componentization

In the early development of iOS Native APP, if there are not many developers involved, most of the codes are written in a project. At this time, business development is not too fast, so development efficiency can be ensured in most cases.

However, once the project is huge, the number of developers will gradually increase, and the business will develop rapidly. At this time, the disadvantages of the single project development mode will be exposed.

  • The code files in the project are severely coupled
  • Conflicts are easy to occur. Large companies have many people developing a project at the same time, and there will be many conflicts every time they pull the latest code. Sometimes it takes about half an hour to merge the code, which will delay the development efficiency.
  • Development on the business side is not efficient enough. There are many developers, each of whom only cares about his own component, but who compile the whole project and mix it up with other irrelevant code. Debugging is not convenient, even if the development of a small function, have to go to the whole project are compiled again, debugging efficiency is low.

To address these issues, the concept of componentization has emerged in iOS projects. So the componentization of iOS is to solve these problems, which are different from the pain points solved by front-end componentization.

IOS componentization brings the following benefits:

  • Speed up compilation (instead of compiling the main and guest chunks of code, each component is a static library)
  • Free choice of development posture (MVC/MVVM/FRP)
  • Facilitate targeted testing by QA
  • Improve business development efficiency

The encapsulation of iOS componentization is only a small part of this, but more concerned with how components are broken up and decoupled. The componentization of the front end may put more emphasis on component encapsulation and high reusability.

How to encapsulate components

The componentization method of iOS is very simple, that is, Cocoapods is used to package into a POD library, and the main project can reference these pods respectively. More and more third-party libraries are releasing their latest versions on Cocoapods, and large companies are maintaining their own private Cocoapods repositories internally. A perfectly packaged Pod component, the main project is very convenient to use.

If you want to use Cocoapods to package a static library or framework, you can use Cocoapods to package a static library or framework.

Ultimately, the ideal goal is that the main project is a shell project, all the other code is inside the component Pods, and the main project’s job is to initialize, load those components, and nothing else.

How to divide components

Although there is no clear standard for dividing components in iOS because each project is different and the coarse-grained component is different, there is a principle for dividing components.

Things like Util, Category, network layer and local storage that can be reused between apps are drawn into the Pod library. Others are business related and reused across apps.

The principle is that code to be shared between apps should be split into Pod libraries and treated as individual components. Business lines that are not shared between apps should also be divided into PODS to decouple them from other files in the project.

Common partition methods are starting from the bottom, network library, routing, MVVM framework, database storage, encryption and decryption, tool class, map, basic SDK, APM, risk control, buried point…… From the bottom up, to the top, there are various business components, the most common ones are like shopping cart, my wallet, login, registration, etc.

Principle of componentization

IOS componentization is based on Cocoapods. For details on how Cocoapods works, see the article “What Does Cocoapods Do?” .

Here is a simple analysis of pod into the library is loaded into the main project.

Pods create Pods workspace by downloading the source code of the dependent libraries in the Podfile. When the program is compiled, the script for the two POD Settings is pre-executed.

In the above script, the packed static library from Pods is merged into the libPods-xxx. a static library, which the main project relies on.

This is the script that loads the Pods library for the main project.

Pods’ other script loads resources. See below.

The resources loaded here are the image resources in the Pods library, or the XIb, Storyboard, music resources in Boudle, etc. These resources are also called into the libPods-xxx. a static library.

The script for loading the resource is shown above.

5. Component classification

IOS components are mainly divided into two forms:

  1. Static library
  2. The dynamic library

Static libraries usually end in.a and.framework files, and dynamic libraries usually end in.dylib and.framework files.

As you can see, a file at the end of the.framework does not tell whether it is a static or dynamic library just by its file type.

The differences between static and dynamic libraries are as follows:

  1. .a files must be static libraries,.dylib must be dynamic libraries,.framework may be static or dynamic libraries;

  2. When a static library is linked to another library, it will be completely copied to the executable file. If multiple apps use the same static library, each App will have a copy of it, which has the disadvantage of wasting memory. Similar to defining a base variable, using the base variable is a new copy of the data, rather than the original definition; The benefits of static libraries are obvious: once compiled, the library files are virtually useless. The target program runs without external dependencies. Of course, its disadvantages are also obvious, that is, the volume of the target program will be increased.

  3. Dynamic library will not be copied, only one copy, the program is dynamically loaded into the memory when running, the system will only load once, multiple programs share a copy, saving memory. And with the dynamic library, you can update the dynamic library file to update the application program without recompiling the connected executable program.

Message passing and state management between components

As we discussed earlier, iOS componentization is very concerned with decoupling, which is an important purpose of componentization. Messaging between iOS components is implemented using routing. About routing, the author has written a more detailed article, interested in the article can see the iOS componentization – Route design ideas analysis.

Vii. Component registration method

There are three ways to register iOS components:

  1. Load method registration
  2. Read plist file registration
  3. Annotation Annotation mode registration

The first two are relatively simple and easy to understand.

The load method uses Runtime to store the mapping between component names and component instances in a global dictionary that can be invoked when the program is started.

The second way is to pre-write the mapping between component names and component instances in a PList file. The program reads the plist file directly when it needs to. The PLIST file can be read from the server so that the App can be more dynamic.

The third way is more dark tech. The Mach-O Data structure is used to write registration information directly into the Data section of the final executable when the program is linked to the executable. After the program is executed, go directly to that section to read the data you want.

For a detailed implementation of these three practices, see my previous article “BeeHive: An elegant but still improving decoupling framework”, which analyzes the implementation of the above three registration processes in detail.


conclusion

Through the above analysis, we can see that the componentization of Vue is quite different from that of iOS.

There are differences in development mode between the two platforms

This is mainly reflected in the difference between single-page application and multi-page application.

One of the most popular front-end applications is single Page Web Application (SPA), which, as the name implies, is a single Web page application. It is a Web application that loads a single HTML page and dynamically updates the page as the user interacts with the application.

The browser loads the initial page from the server, along with the scripts (frameworks, libraries, application code) and style sheets required for the entire application. When the user locates to another page, the page refresh is not triggered. Update the page URL via the HTML5 History API. The browser makes AJAX requests to retrieve new data needed for a new page, usually in JSON format. SPA then dynamically updates the new page that has already been downloaded in the initial page load via JavaScript. This model is similar to how native mobile apps work.

However, iOS development is more like MPA (Multi-Page Application).

For a native App, the page should look something like this. Of course, one could argue that it’s still possible to write this many pages as a single page, controlling all the views in a VC, just like the FRONT-END DOM. Although this idea is feasible in theory, but I have not seen someone to do so, more than a page, more than 100 pages, thousands of views, all in a VC control, so the development of a bit of pain.

They also address different needs

The componentalization of iOS partly solves the problem of code reuse, but mostly solves the problem of large coupling and low development efficiency and cooperation. The componentization of Vue is more about solving the problem of code reuse.

The direction of componentization is also different.

The iOS platform has a Framework such as UIKit that Apple has already packaged, so the basic controls have been packaged, and we do not need to manually package them, so the iOS components focus on a large function, such as network library, shopping cart, my wallet, the whole business block. Front-end page layout is carried out in the DOM, only the most basic CSS tags, so the controls need to write their own, Vue’s component-based packaging reusable single file components are actually more similar to the iOS side of the ViewModel.

So in terms of encapsulation, there is not much to learn from each other. One thing iOS can borrow from the front end is in the area of state management, the idea of one-way data flow. However, although this idea is good, but how to get better practice in their own company’s APP is still a matter of opinion, not all businesses are suitable for one-way data flow.


Reference: vue. js official document

Making Repo: Halfrost – Field

Follow: halfrost dead simple

Source: halfrost.com/vue_ios_mod…