Two years ago, I just got into mobile terminal development. I was confused at the beginning. Every time I met a problem, I would ask questions in the community or share the experience of my predecessors. This article combines the previous articles and development experience to share with you, hoping to help new people who just entered the mobile terminal development to solve their doubts. One of these will be used belowProvident Fund pageAs an example, the development project introduces some mobile terminalsQ&AAnd makeMulti-page development using Vuejs as a libThe experience.

Mobile adaptive layout

The most commonly used mobile adaptive layout scheme in the project is Flexbox combined with REM. The standard column type uses Flexbox, and most of the other irregular views use REM. The most common scheme for REM is taobao’s open source scalable layout scheme.

Set scale (scale = 1 / deviceRatio) according to the device pixel ratio, so that the viewport device-width is always equal to the device physical pixel, and then dynamically calculate the root font size according to the screen size. Specifically, divide the screen into 100 equal parts, each of which is a. One rem is equal to 10a.

mark

We usually get 750 wide designs, which are based on the physical resolution of the iPhone6. Some designers may be lazy, there is no mark on the design drawing, if we develop while measuring the size, undoubtedly the efficiency is relatively low. Either let the designer label it, or do it yourself.

If designers really don’t have the time, we recommend markman for annotation. The free version does not have some features (such as not being able to save locally) but basically meets our needs.

Then I found a better annotation tool than Markman, PxCook, which shows the layer style code from the PSD design, which is handy for the front end.

After annotation, we started to write our style. After using Taobao’s Lib-Flexible library, our base value of the root font was 750/100*10 = 75px. If a label in the image is 100px, the CSS should be set to 100/75 = 1.333333rem. Therefore, in order to improve the development efficiency, we can use the PLUG-IN that transforms PX into REM. Here is the sublimeText/Vscode conversion plugin:

Px to REM plugin

Use some summary of REM

  • In all units, it is recommended to use PX for font size, and then control important points in conjunction with the media query. This can be used to highlight or weaken certain fonts rather than adjust them as a whole.
  • The directional units can be all PX and the horizontal ones REM, because the mobile device has limited width and the height can slide down indefinitely. However, there are exceptions to this. For example, for some active registration pages that need to be fully displayed in one screen without a drop-down, all directional or horizontal should use REM as the unit. As shown in figure:

The height units of the form on the left are much better when using PX on different screens due to the larger margins at the bottom. The active registration page on the right should use REM for all directional height, margin, and padding since there is no scroll bar.

  • Some effects such as border, box-shadow, border-radius, etc. should use px units.

Mobile phone status bar and browser navigation bar

In an article published before, there was a question raised by a front-end friend of SF: All covered in this paper, the author has emphasized layout, and the deal with a lot of space below is different, I met this kind of situation in the work, the designer’s design draft width is 750 x 1334, but the actual display height is not so much, because have a navigation bar above include mobile phone status bar display, so the overall height to reach 750, However, the designer’s design draft was designed strictly in accordance with 750. In this case, if REM is used and the design is restored strictly in accordance with the designer’s size, there will be a situation of scroll bars on the screen. How do you deal with this situation? From the design specification, or from the development of the corresponding measures

Again, take my sharing interface as an example: the display height difference usually occurs between wechat and the browser, because the former has no address bar and toolbar, so the display height is usually consistent with the designer’s view. If we follow the pure padding and margin, even if we use REM, it will still exceed one screen height on the browser side, which is not what we want to see for sharing pages. This was a trade-off, and I used absolute positioning for the main area so that the upper gap was small but still remained at one screen height. If margin padding is used, the scroll bar must already appear. Of course this is dependent on the premise of design, designers often to space has retained a certain gap, also will not be the main object height is set too high, or too hold full not good-looking, also on the development without drawing wide high within a certain limit, beyond cannot avoid, but we share this interface is usually through WeChat share friends, It doesn’t really matter that a scrollbar appears in a view opened through the browser, does it? Attached below are the renderings of wechat and browser:

WeChat side:

Browser side:

Vuejs develops mobile pages as lib

Why not use SPA mode

Generally, mobile terminals use VUE for pages with frequent data interaction and rapid development. Why not use single-page SPA development mode? There are several reasons.

  • For fast development, fast online
  • Other members of the project are not familiar with SPA and webpack
  • When I participated in the project, the project had been developed with multiple pages and could not be reconstructed in a short time

Instead of using a single-page architecture, when developing a multi-page application, one page interaction logic corresponds to one Vue instance.

Property injection based on the data returned by the interface

Putting aside the concept of “property injection based on interface return data” as a personal creation, let’s talk about how form data is bound.

Data binding for forms

An important point is to separate several form objects from several forms for data binding.

The above figure provident fund query as an example, because different cities will have different query elements, may be logged only one way, there may be several. For example, there are three ways to log in, and two ways to log in when using a VUE layout.

  • 1. Create only one form for data binding and click the button to trigger judgment
  • 2. There are several login methods to create several forms with a field identifying the currently displayed form

Due to the use of third-party interface, initially did not return to interface data structure view, the first kind of the wrong way, the wrong one is below the number of elements on each landing way is different also, mistake is data binding in the same form data, when the user after the user name login way to enter the user name password, switch to the customer number of landing, You can have data confusion.

After solving the layout problem, we need to define some states according to the design diagram, such as the current login mode switch, the consent authorization state switch, the button can be clicked state, whether in the request state. And of course there’s some data that the app passes through, which I’m ignoring here.

data: {
    tags: {
        arr: [''],
        activeIndex: 0
    },
    isAgreeProxy: true,
    isLoading: false
}
Copy the code

After reviewing the data returned by the interface, it is recommended to use chrome plugin Postman, such as Hohhot login elements as follows:

{" code ": 2005," data ": [{" name" : "login_type", "label" : "id", "fields" : [{" name ":" user_name ", "label" : "Id", "type" : "text"}, {" name ":" user_pass ", "label" : "password", "type" : "password"}], "value" : "1"}, {" name ": "Login_type", "label" : "accumulation fund account", "fields" : [{" name ":" user_name ", "label" : "accumulation fund account", "type" : "text"}, {" name ": "User_pass", "label" : "password", "type" : "password"}], "value" : "0"}], "message" : "request is successful login elements"}Copy the code

We define a loginWays in data, which is initially an empty array, and then define a request interface function in Methods, which is to inject an input field into the above fields object based on the returned data for binding. This is known as property injection based on the data returned by the interface.

methods: { queryloginWays: function(channel_type, channel_code) { var params = new URLSearchParams(); params.append('channel_type', channel_type); params.append('channel_code', channel_code); axios.post(this.loginParamsProxy, params) .then(function(res) { console.log(res); var code = res.code || res.data.code; var msg = res.message || res.data.message; var loginWays = res.data.data ? res.data.data : res.data; // Query failed if (code! = 2005) { alert(msg); return; ForEach (function(loginWay) {loginway.fields. ForEach (function(field) {field.input = "'; }) }) this.loginWays = loginWays; this.tags.arr = loginWays.map(function(loginWay) { return loginWay.label; }) }.bind(this)) } }Copy the code

It doesn’t matter if the data we return has data we don’t need, so we don’t lose the data we need for the next login.

So how do you transfer data from page to page? If it is passed by the app, it usually uses URL concatenation, obtains queryString using window.location.search and then intercepts it. ${field name} ${field name} ${field name} ${field name}

The computed: {/ / real name realName: function () {return enclosing getQueryVariable (' name ') | | '}, / / id card identity: function() { return parseInt(this.getQueryVariable('identity')) || '' }, /*If javaWeb realName: function() { return this.getQueryVariable('name') || '' }, identity: function() { return parseInt(this.getQueryVariable('identity')) || '' }*/ }, methods: { getQueryVariable: function(variable) { var query = window.location.search.substring(1); var vars = query.split('&'); for (var i = 0; i < vars.length; i++) { var pair = vars[i].split('='); if (decodeURIComponent(pair[0]) == variable) { return decodeURIComponent(pair[1]); } } console.log('Query variable %s not found', variable); }}Copy the code

About front-end cross-domain debugging

When making interface requests, our pages are usually previewed on sublime’s local server or vscode’s local server, so the request interface can run into cross-domain issues, using the plug-in http-proxy-middleware if packaged using Gulp, or using nginx.

Using Gulp

During project construction we usually put the source code in the SRC folder and use gulp for compression, merging, image optimization (as needed), etc. We use gulp.

Cross-domain problems can be solved with gulp-Connect in conjunction with HTTP-proxy-Middleware, where we preview debugging on a local server in gulp-Connect.

Use gulp server:dev to listen for file changes and use livereload to refresh. Use gulp command to package; Use gulp Server :dist to proxy the dist production directory.

var gulp = require('gulp'); var concat = require('gulp-concat'); var uglify = require('gulp-uglify'); var autoprefixer = require('gulp-autoprefixer'); var useref = require('gulp-useref'); var connect = require('gulp-connect'); var proxyMiddleware = require('http-proxy-middleware'); Localhost :8088/ API to https://api.xxxxx.com/ gulp.task('server:dev', ['listen'], function() { var middleware = proxyMiddleware(['/api'], { target: 'https://api.xxxxx.com/', changeOrigin: true, pathRewrite: { '^/api': '/' } }); connect.server({ root: env == 'dev' ? './src' : './dist', port: 8088, livereload: true, middleware: function(connect, opt) { return [middleware] } }); }); Gulp.task ('server:dist', ['listen'], function() {var middleware = proxyMiddleware(['/ API '], {target: 'https://api.xxxxx.com/', changeOrigin: true, pathRewrite: { '^/api': '/' } }); connect.server({ root: './dist', port: 8088, livereload: true, middleware: function(connect, opt) { return [middleware] } }); }); gulp.task('html', function() { gulp.src('src/*.html') .pipe(useref()) .pipe(gulp.dest('dist')); }); gulp.task('css', function() { gulp.src('src/css/main.css') .pipe(concat('main.css')) .pipe(autoprefixer({ browsers: ['last 2 versions'], cascade: false })) .pipe(gulp.dest('dist/css/')); gulp.src('src/css/share.css') .pipe(concat('share.css')) .pipe(autoprefixer({ browsers: ['last 2 versions'], cascade: false })) .pipe(gulp.dest('dist/css/')); gulp.src('src/vendors/css/*.css') .pipe(concat('vendors.min.css')) .pipe(autoprefixer({ browsers: ['last 2 versions'], cascade: false })) .pipe(gulp.dest('dist/vendors/css')); return gulp }); gulp.task('js', function() { return gulp.src('src/vendors/js/*.js') .pipe(concat('vendors.min.js')) .pipe(uglify()) .pipe(gulp.dest('dist/vendors/js')); }); gulp.task('img', function() { gulp.src('src/imgs/*') .pipe(gulp.dest('dist/imgs')); }); gulp.task('listen', function() { gulp.watch('./src/css/*.css', function() { gulp.src(['./src/css/*.css']) .pipe(connect.reload()); }); gulp.watch('./src/js/*.js', function() { gulp.src(['./src/js/*.js']) .pipe(connect.reload()); }); gulp.watch('./src/*.html', function() { gulp.src(['./src/*.html']) .pipe(connect.reload()); }); }); gulp.task('default', ['html', 'css', 'js', 'img']);Copy the code

Use nginx

If you use proxy_pass in nginx configuration, you need to note that if you add/to the url after proxy_pass, it indicates the absolute root path. If there is no /, it indicates the relative path, and the matched part of the path is also passed to the agent.

server { listen 80; server_name localhost; root [Your project root]; index index.html index.htm default.html default.htm; location ^~/api { proxy_pass https://api.xxxxx.com/; }}Copy the code

Public account webpage debugging

If you develop H5 based on wechat jsSDK, you must have contact with wechat authorized domain name, wechat will send authorization data to a callback page, and the callback page must be under your configured domain name (including subdomain name). For example, we get the user’s OpenID operation. Check a xxx_verify_xxx.txt file in the root directory of the domain name to ensure that the domain name belongs to you.

Therefore, in order to obtain OpenID in wechat development and debugging tool, we need to use a tool called Intranet penetration. Here are two of my favorite tools:

  • ngrok
  • Peanut shells

ngrok

Ngrok executes the command

ngrok -config ngrok.cfg start web
Copy the code

In the ngrok.exe directory requires a configuration file ngrok. CFG the following is an example of the configuration:

server_addr: "tunnel.cn:4443"
trust_host_root_certs: false
tunnels:
  web:
   subdomain: "xxx" 
   proto:
    http: 8086
    https: 8086
Copy the code

Xxx.tunnel. cn:4443 will point to your local port 8086. Put xxx_verify_xxx. TXT in the root directory of port 8086 to configure the authorized domain name successfully.

Peanut shells

The free version of peanut Shell costs only 6 yuan for individuals to open, and then provides you with 1 gigabyte of data per month. The free version does not support port 80, supports up to two domain names, and requires downloading a desktop client.

Adding a domain name mapping is simple, the free version cannot configure custom domain names, which are automatically generated by the peanut shell.



After the configuration is complete, you can start the client to view the current status

Thanks for reading, welcome any form of technical questions and exchanges!