Navigation Website Collection and Navigation Platform

original intention

It is estimated that many people, like minor editors, initially collect the websites they have visited into the browser's collection folder. Over the years, there are more and more websites, and it is more and more difficult to find the required websites in the collection folder. Later, the browser moved from Firefox to Chrome, but the collection site could not be synchronized. Browsers need to log in to synchronize their favorites, which is painful... For various reasons, such a collection, search and classification navigation platform is really great, Xiaobian has made an open source project - Navigation website collection and navigation platform.

Implementing Functions

Website CRUD

search

Log in and log out

Screenshots of the website

Site Navigation Block Waterfall Flow

Web site nested iframe and search module

Website Submission Page

Online Demo

preview

A Simple Way to Realize

Local Construction

<!-- Download project -->
git clone https://github.com/qiufeihong2018/navigation-server.git

<!-- Installation dependency -->
npm install

<!-- Crawling data -->
node ./creeper/index.js

<!-- Start the program -->
npm run dev

Then visit http://localhost:1600

back-end

navigation-server back-end code repository

express-based framework

express Building web Applications

Features

  • Powerful routing
  • Focus on high performance
  • Ultra-high test coverage
  • HTTP assistant (redirection, caching, etc.)
  • View the system that supports the 14 + template engine
  • Content negotiation
  • Quick generation of executable files for applications

analysis

Start the express service

const express = require('express');
const app = express();
const config = require('../config')();

  // start server
  // Set http port
  app.set('port', config.expressHttpPort); 

  app.listen(config.expressHttpPort, () => {
    // Open Port Print Log
    log.info(`express running on ${config.expressHttpPort} port`);
  });

Dynamically configuring ports in config files

The main way to do this is to get rid of all kinds of patterns.

'use strict';

var config = {
  development: {
    // mongodb
    database: 'mongodb://localhost/map',
    expressHttpPort: 1600,
    logFile: './log/express.log'
  },
  local: {
    // mongodb
    database: 'mongodb://127.0.0.1/map',
    expressHttpPort: 1600,
    logFile: './log/express.log'
  },
  production: {
    // mongodb
    database: 'mongodb://127.0.0.1/map',
    expressHttpPort: 1600,
    logFile: './log/express.log'
  }
};


module.exports = function(mode) {
  var env;
  if (!mode) {
    env = process.env.NODE_ENV || 'development';
  } else if (mode && (mode === 'development' || 'local' || 'production')) {
    env = mode;
  } else {
    throw new Error(`config can only be 'development' || 'local' || 'production', 
    but you give ${mode}`);
  }
  var returnVal = config[env];
  return returnVal;
};

express-session express Simple session Middleware

Features

  • resave: Even if the session has never been modified during the request, it is mandatory to save the session back to the session store. Depending on your store, this may be necessary, but it can also create race conditions, where the customer lets two parallel requests to your server, and changing the session in one request may override the end of another request, even if it does not change. The default value is true.
  • saveUninitialized: Forces "uninitialized" sessions to be saved to storage. When the session is new but not modified, it is uninitialized. Selecting false is very useful for implementing login sessions, reducing server storage usage, or abiding by laws that require permission before cookie s are set up. Choosing false can also help resolve the contention conditions for clients to issue multiple parallel requests without a session. The default value is true, but it is not recommended because the default value will be changed in the future.
  • Secret: This is the password used to sign the session ID cookie. This can be a single secret string or an array of multiple secrets. If a secret array is provided, only the first element is used to sign the session ID cookie, and all elements are considered when verifying the signature in the request.
  • Cookie: Each session has a unique cookie object. This allows you to change the session cookie for each visitor.

    • maxAge: maxAge will return the remaining time (in milliseconds), and the editors can reassign a new value to adjust the. expires attribute appropriately. This means that it will expire one day later.

analysis

const session = require('express-session');


  // Session configuration
  const sess = {
    resave: true,
    saveUninitialized: true,
    secret: 'I am hungry',
    cookie: {
      maxAge: 24 * 60 * 60 * 1000
    }
  };


  app.use(session(sess)); // Set session middleware

To know more about the configuration, see the translation before Editor express-session

body-parser Text Analysis

Features

  • It is a Node.js text parsing middleware.

analysis

Before the handler, the incoming request body is parsed using middleware, which is available under the req.body attribute.

Note that since the req.body shape is based on user-controlled input, all attributes and values in the object are untrustworthy and should be validated before trust. For example, req.body.foo.toString() may fail in a variety of ways, such as foo attributes may not exist or may not be strings, and toString may not be a function, but a string or other user input.

  • urlenencoded: ([options]) Returns the middleware, which parses only the urlencoded body and only looks at requests for content type headers that match type options. The parser only accepts UTF-8 encoding of text and supports automatic expansion of gzip and deflate encoding. After the middleware (req.body), fill a new body object with parsed data on the request object. This object will contain key-value pairs, where values can be strings or arrays (when extended to false) or any type (when extended to true).

    • Extended: The option allows you to choose between parsing url-encoded data using querystring libraries (when false) and using qs libraries (when true). The extended syntax allows rich objects and arrays to be encoded in url encoding format, and allows the use of a json-like url encoding experience.
const bodyParser = require('body-parser');

......

  // parse application/x-www-form-urlencoded
  app.use(bodyParser.urlencoded({
    extended: false
  }));

  // parse application/json
  app.use(bodyParser.json());

mongoose Connect to the database

Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment.

Features

  • stack overflow
  • bug report
  • mongoose Slack Channel
  • Help Forum
  • MongoDB support

analysis

Connect to the database to process the success and failure of the connection.

'use strict';

const mongoose = require('mongoose');
const config = require('../config')();
// [koa warns Deprecation Warning: Mongoose: `FindOne AndUpdate ()` and `FindOne AndDelete ()` without the `use...] (https://www.jianshu.com/p/f3128e7ae3c5)
mongoose.set('useFindAndModify', false);
let reconnectTimes = 0;// Mongodb reconnect times
let reconnectInterval = 0.1;// The interval seconecd time between two reconnection;
const maxReconnectInterval = 120;// The max interval time between two reconnection;

// Connect to mongodb
function connect() {
  const options = {
    socketTimeoutMS: 3000,
    keepAlive: true,
    reconnectTries: 4,
    useNewUrlParser: true
  };
  mongoose.connect(config.database, options);
}

// Mongoose error handler
mongoose.connection.on('error', function(err) {
  log.error(err);
});

// Mongoose reconnect when closed
mongoose.connection.on('disconnected', function() {
  reconnectTimes++;
  reconnectInterval = reconnectInterval * 2;
  if (reconnectInterval > maxReconnectInterval) reconnectInterval = maxReconnectInterval;
  setTimeout(() => {
    connect();
  }, reconnectInterval * 1000);
});

mongoose.connection.on('connected', function() {
  reconnectTimes = 0;
  reconnectInterval = 0.1;
});

exports.connect = connect;

Create database collection AdminMap

'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const AdminMap = new Schema({
  category: { type: String, required: true, trim: true },
  name: { type: String, required: true, trim: true },
  website: { type: String, required: true, trim: true },
  describe: { type: String, trim: true },
  logo: { type: String, trim: true },
  way: { type: String, trim: true },
}, {
  timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }
});


module.exports = mongoose.model('AdminMap', AdminMap);

eslint Specification code

Features

  • ESLint is a tool for identifying and reporting patterns in ECMAScript / JavaScript code. In many ways, it is similar to JSLint and JSHint, with some exceptions:
  • ESLint uses Espree for JavaScript parsing.
  • ESLint uses AST to evaluate patterns in code.
  • ESLint is completely pluggable, and each rule is a plug-in that adds more at runtime.

To know more about the configuration, see the translation before Editor Configuration of eslint in express

cheerio crawls data

cheerio Reptile

Fast, flexible and streamlined core jQuery implementation designed specifically for servers.

request

Simple http request client

Detailed usage can be found in the article before Xiaobian. "node Crawls Desktop Wallpaper of a Photo Website"

pm2 daemon project

"pm2": "pm2 start index.js --name='navigation'"

For more details, please see the article before Xiaobian. <pm2>

mocha test

mocha

Simple, flexible and interesting javascript testing framework for node.js and browsers

mochawesome

Mochawesome is a custom reporter for the Javascript testing framework mocha. It runs on Node.js and is used in conjunction with mochawesome-report-generator to generate independent HTML/CSS reports to help visualize your test run.

should

BDD-style assertions for node.js

It is an expressive, readable, framework-independent assertion library. The main purpose of this library is to express and help. It keeps your test code clean, and your error messages are useful.

supertest

Used to test node.js HTTP server using a fluent API.

For more details, please see the article before Xiaobian. express Project Integration mocha testing framework

passport username and password authentication

These three are closely related. The first two can be managed by passport-local-mongoose. The main parsing is in the dependency package of passport-local-mongoose.

passport

Features

Passport is Node.js Express compatible authentication middleware.

Passport's sole purpose is to validate requests through a set of extensible plug-ins called policies. Passport does not mount routing or assume any specific database architecture, which maximizes flexibility and allows developers to make application-level decisions. Passport provides hooks to control the success or failure of authentication.

  • Session: Passport maintains a persistent login session. In order for a persistent session to work, authenticated users must be serialized into the session and deserialized when subsequent requests are made. Passport has no restrictions on how user records are stored. Instead, you provide Passport with functions that implement the necessary serialization and deserialization logic. In a typical application, this is as simple as serializing user IDs and finding users by ID when deserializing.
  • Initialize: To use Passport in Express-based or connection-based applications, configure it using the required passport.initialize() middleware. If your application uses a persistent login session (recommended, but not required), you must also use the passport.session() middleware.

passport-local

Features

Passport policy for authentication using username and password.

This module allows you to authenticate using the username and password in the Node.js application. By inserting Passport, local authentication can be easily and inconspicuously integrated into any application or framework that supports Connect-style middleware, including Express.

Before doing validation, we need to configure the strategy first.

passport.use(new LocalStrategy(
  function(username, password, done) {
    User.findOne({ username: username }, function (err, user) {
      if (err) { return done(err); }
      if (!user) { return done(null, false); }
      if (!user.verifyPassword(password)) { return done(null, false); }
      return done(null, user);
    });
  }
));

passport-local-mongoose

Features

passport-local-mongoose is a Mongoose plug-in that simplifies the use of Passport to build user names and passwords

analysis
  1. First, you need to import dependency packages into the schema.
const passportLocalMongoose = require('passport-local-mongoose');

const options = {
  interval: 200,
  maxInterval: 6 * 60 * 1000,
  maxAttempts: 6,
  limitAttempts: true
};
User.plugin(passportLocalMongoose, options);
  1. Configure Passport and Pasport-Local

It can simplify the configuration of both.

passport-local-mongoose can be configured by setting LocalStrategy, serializeUser, and deserializeUser

Analytical Views on Specific Parameters passport-local-mongoose of Mongoose

  // requires the model with Passport-Local Mongoose plugged in
  var User = require('../collections/user');
  app.use(passport.initialize());
  app.use(passport.session());
  // use static authenticate method of model in LocalStrategy
  passport.use(new LocalStrategy(User.authenticate()));
  // use static serialize and deserialize of model for passport session support
  passport.serializeUser(User.serializeUser());
  passport.deserializeUser(User.deserializeUser());

winston logs

winston Logging

  • winston is designed as a simple and universal log library, which supports multiple transmissions. Transport is essentially a log storage device. Each winston recorder can have multiple transmissions configured at different levels. For example, you might want to store error logs in persistent remote locations, such as databases, but all logs are exported to the console or local files.
  • winston aims to separate some logging processes and make them more flexible and extensible. Note the flexibility of supporting log formats and levels, and ensure that these API s are separated from the implementation of transporting log records

To know more about the configuration, see the translation before Editor <winston>

winston-daily-rotate-file

winston's transfer is recorded to the rotating file. Rotation logs can be limited by date and size, and old logs can be deleted by count or by number of days passed.

To know more about the configuration, see the translation before Editor <winston-daily-rotate-file>

Encapsulating winston log, when in development mode, the generated log exists in express.log and the log level is debug; when in production mode, there is a timestamp log, the log level is info, which can store 7 days of files, the maximum file can not exceed 20 megabytes; other mode log level is also info.

'use strict';

/**
 * Logger is to custom winston to provide different log pattern in 'development',
 * 'production' and other mode.
 * 'development' will use Console and File output with 'debug' level
 * 'production' will use DailyRotateFile output with 'info' level,
 *  and the maxFiles is 7d.
 *  other mode will use File output with 'info' level.
 */
const {
  createLogger,
  format,
  transports
} = require('winston');
const {
  combine,
  timestamp,
  label,
  printf
} = format;

require('winston-daily-rotate-file');
const config = require('../config')();
const MODE = require('../constant/system').MODE;
let mode = process.env.NODE_ENV;
if (!mode) mode = MODE.DEVE;

let logFile = config.logFile;

logFile = logFile.replace('.log', ''); // remove '.log' from the logFile

const trans = [];
const ts = {
  console: new transports.Console({
    level: 'debug'
  }),
  file: new transports.File({
    filename: `${logFile}.log`,
    level: 'info'
  })
};
// daily rotate file transport config
const dailyRotateFileTrans = new (transports.DailyRotateFile)({
  filename: `${logFile}-%DATE%.log`,
  datePattern: 'YYYY-MM-DD-HH',
  zippedArchive: true,
  maxSize: '20m',
  maxFiles: '7d'
});
// Dynamically change the log level of the transfer
if (mode === MODE.DEVE) {
  trans.push(ts.console);
  ts.file.level = 'debug';
  trans.push(ts.file);
} else if (mode === MODE.PROD) {
  trans.push(dailyRotateFileTrans);
} else {
  trans.push(ts.file);
}
exports.createLogger = function(source) {
  const myFormat = combine(
    label({
      label: source
    }),
    timestamp({
      format: 'YYYY-MM-DD HH:mm:ss'
    }),
    printf(({
      level,
      message,
      label,
      timestamp
    }) => {
      return `${timestamp} [${label}][${level.toUpperCase()}]: ${message}`;
    })
  );
  return new (createLogger)({
    format: myFormat,
    transports: trans
  });
};

CRUD

There's nothing to say about the business logic of adding, deleting, and modifying. The code is in the warehouse.

Just pay attention to one point:

This is where get requests are made to think about database requests for data on a page of a certain category of website. limit and other key words are separated from req._parsedOriginalUrl.query.

To get the total length, look it up twice here.

router.get('/', function(req, res) {
  const arr = req._parsedOriginalUrl.query.split('&');
  const limit = arr[0].split('=')[1];
  const offset = arr[1].split('=')[1];
  const cate = arr[2].split('=')[1];
  let total = 0;
  SuperAdminMap.find({ category: cate }).then((data) => {
    total = data.length;
    SuperAdminMap.find({ category: cate })
    .limit(Number(limit))
    .skip(Number(offset))
    .then((data) => {
      log.info(`Get ${cate} data`);
      res.status(200).json({
        data,
        total
      });
    });
  });
});

apidoc document artifact

In order to view the api easily, it is absolutely necessary to use apidoc

To know more about the configuration, see the translation before Editor "apiDoc Generates Interface Documents with Easy Effort"

Here is a comment on the get request for the back end to find the superAdmin database

/**
 * @api {get} /superAdmin/ SuperAdmin getMap
 * @apiName SuperAdminGet
 * @apiGroup superAdminOperation
 *
 * @apiParam {String} limit  Number of pages per page.
 * @apiParam {String} offset  Number of skips.
 * @apiParam {String} category  New website's category.
 *
 *
 * @apiSuccessExample Success-Response:
 *     HTTP/1.1 200 OK
 *{
 *    "data": [
 *        {
 *            "_id": "5d5e4206443bdd63d0f82327",
 *            "category": "recommendationFront-end",
 *            "name": "test1",
 *            "website": "test4",
 *            "describe": "test",
 *            "logo": "test",
 *            "created_at": "2019-08-22T07:19:34.924Z",
 *            "updated_at": "2019-08-22T07:19:34.924Z",
 *            "__v": 0
 *        },
 *        {
 *            "_id": "5d5e4209443bdd63d0f82328",
 *            "category": "recommendationFront-end",
 *            "name": "test1",
 *            "website": "test5",
 *            "describe": "test",
 *            "logo": "test",
 *            "created_at": "2019-08-22T07:19:37.430Z",
 *            "updated_at": "2019-08-22T07:19:37.430Z",
 *            "__v": 0
 *        }
 *    ],
 *    "total": 655
 *}
 * @apiError NOT_LOGIN The current User was not logon.
 *
 * @apiErrorExample Error-Response:
 *     HTTP/1.1 401 Unauthorized
 *     {
 *       "err": "NOT_LOGIN",
 *       "message": "User has not logon in!"
 *     }
 */

Generate api document after executing npm run apidoc command

Front end

navigation-web front-end code repository

It's based on flowery pants. vue-admin-template Simple version of the background management model, which is based on vue2.0 background management platform popular.

Vuex Storage state

Features

Vuex is a state management model developed specifically for Vue.js applications. It uses centralized storage management to manage the state of all components of an application, and ensures that the state changes in a predictable manner with corresponding rules.

analysis

Automatically import files from modules folder

Recommend a regular Handbook of Lao Yao JavaScript Regular Expressions Mini Book (Version 1.1). pdf

  • ^ Match the beginning of a line in a multi-line match.
  • The $(dollar sign) matches the end and matches the end of the line in a multi-line match.
  • ^,$,.,*,+,?,|,,/,(,),[,],{,},=,!,:,- ,

When matching the above characters themselves, they can all be escaped:

  • w denotes [0-9a-zA-Z_]. Represents numbers, upper and lower case letters, and underscores.

Memory: w is short for word, also known as word characters.

  • + Equivalent to {1,}, indicating at least one occurrence.

Memory: Plus is the meaning of additions. You have to have one before you consider additions.

Match all files according to the rule (find the file ending with js in the modules folder)

  • replace a new string

// https://webpack.js.org/guides/dependency-management/#requirecontext
const modulesFiles = require.context('./modules', true, /\.js$/)

// you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  // set './app.js' => 'app'
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
  const value = modulesFiles(modulePath)
  modules[moduleName] = value.default
  return modules
}, {})

const store = new Vuex.Store({
  modules,
  getters
})

axios Carry out front-end and back-end data communication

Features

Support HTTP data communication. Axios is a promise-based HTTP library that can be used in browsers and node.js.

Axios is recommended by Youda, which has brought Axios into the eyes of many people. Axios is essentially an encapsulation of native XHR, but it is an implementation version of Promise, which conforms to the latest ES specification.

  • Client support prevents CSRF. Each request carries a key from cookie. According to the browser homology strategy, the counterfeit website can not get the key from cookie. In this way, the background can easily distinguish whether the request is misleading input from the user on the counterfeit website and adopt the correct strategy.
  • After the login is completed, the user's token is stored locally through cookie s, and then the token is intercepted and read before the page jumps. If the token exists, it indicates that the user has logged in and refreshes the token status in vuex. Each time a request is sent, it carries a token. The back end determines whether to log in or expire by carrying a token.

analysis

In the request file encapsulating the Axios object, the custom status code is removed from the response response.

import axios from 'axios'
import {
  Message
} from 'element-ui'
// production
import store from '@/store'
import {
  getToken
} from '@/utils/auth'

// create an axios instance
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 5000 // request timeout
})

// request interceptor
service.interceptors.request.use(
  config => {
    if (process.env.NODE_ENV === 'production' && store.getters.token) {
      // do something before request is sent
      // let each request carry token
      // ['X-Token'] is a custom headers key
      // please modify it according to the actual situation
      config.headers['X-Token'] = getToken()
    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

// response interceptor
service.interceptors.response.use(
  /**
   * If you want to get http information such as headers or status
   * Please return  response => response
   */

  /**
   * Determine the request status by custom code
   * Here is just an example
   * You can also judge the status by HTTP Status Code
   */
  response => {
    const res = response.data
    return res
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service

element-ui Quickly build the backstage

Features

Hungry web Platform UI Library

Element, a desktop component library based on Vue 2.0 for developers, designers and product managers

analysis

Importing element-ui globally in main.js

import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
// import enLocale from 'element-ui/lib/locale/lang/en'
import zhLocale from 'element-ui/lib/locale/lang/zh-CN'
// set ElementUI lang to EN
Vue.use(ElementUI, {
  zhLocale
})

el-breadcrumb breadcrumb crumb

Features

Show the path of the current page and quickly return to any previous page.

analysis

  <el-breadcrumb class="app-breadcrumb" separator=">">
    <transition-group name="breadcrumb">
      <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
        <span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
        <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
      </el-breadcrumb-item>
    </transition-group>
  </el-breadcrumb>

el-drawer drawer component pops up search information

analysis

The search bar controls the underlying drawer components by changing the open Drawer state in vuex. In the pop-up drawer, you can search the title and description of the navigation website in the mongo database by keywords, click iframe and the external chain to view the collection website.

   <el-drawer title="Search websites" :visible.sync="openDrawer" :before-close="closeDrawer" direction="btt" size="50%">
      <div class="search-container">
        <el-input slot="prepend" v-model="queryData.query" placeholder="Please enter, for example: ppt" @keyup.enter.native="getSuperSearch">
          <el-button slot="append" icon="el-icon-search" @click.stop="getSuperSearch" />
        </el-input>
      </div>
      <el-table :data="tableData" stripe style="width: 100%" highlight-current-row>
        <el-table-column type="index" />
        <el-table-column prop="name" label="Name" width="200" show-overflow-tooltip />
        <el-table-column prop="website" label="Website Links" width="200" show-overflow-tooltip>
          <template slot-scope="slot">
            <router-link class="font-website" :to="{ path: 'iframeNav', query: { website: slot.row.website }}">
              {{ slot.row.website }}
            </router-link>
          </template>
        </el-table-column>
        <el-table-column prop="describe" label="describe" show-overflow-tooltip />
        <el-table-column prop="created_at" label="Creation time" width="200" show-overflow-tooltip />
        <el-table-column prop="category" label="classification" width="200" show-overflow-tooltip />
        <el-table-column fixed="right" label="operation" width="100">
          <template slot-scope="scope">
            <router-link class="font-website" :to="{ path: 'iframeNav', query: { website: scope.row.website }}">
              iframe link
            </router-link>
            <a class="font-website" :href="scope.row.website" target="_blank">New window links</a>
          </template>
        </el-table-column>
      </el-table>
      <div class="pagination-container">
        <el-pagination small background layout="prev, pager, next" :total="total" :page-size="2" @current-change="handleCurrentChange" />
      </div>
    </el-drawer>

js-cookie Processing Browser cookie s

Features

A simple, lightweight JavaScript API for handling browser cookie s

  • Applicable to all browsers
  • Accept any role
  • After rigorous testing
  • No dependence
  • Unobvious JSON support
  • Support AMD/CommonJS
  • Compliance with RFC 6265
  • Enable custom encoding/decoding

CRUD cookie s

import Cookies from 'js-cookie'

const TokenKey = 'navigation_token'

export function getToken() {
  return Cookies.get(TokenKey)
}

export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}

normalize.css

Features

High consistency across browsers is provided on the default HTML element style. Compared with traditional css reset, Normalize.css is a modern, high-quality alternative to HTML5.

  • Unlike many CSS resets, retain useful defaults rather than delete them.
  • Standardize the styles of various elements.
  • Corrected errors and common browser inconsistencies.
  • Improve availability through subtle modifications.
  • Use detailed comments to illustrate the role of the code.

Recommended reading What are the differences between Normalize.css and traditional CSS Research?

nprogress Progress bar

Features

Ultra-thin progress bar

analysis

The progress bar is controlled by calling start() and done().

Used for permission page jumps

import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
NProgress.configure({
  showSpinner: false
}) // NProgress Configuration

NProgress.start()

NProgress.done()

You can also adjust the speed.

NProgress.configure({ easing: 'ease', speed: 500 });

Close the load fine tuner. (Default: true)

NProgress.configure({
  showSpinner: false
}) // NProgress Configuration

Change its parent container

NProgress.configure({ parent: '#container' });

path-to-regexp Address and parameter processing in url

Features

The tool library is used to process the address and parameters in url, and it is very convenient to get the data that small editors want.

In js, RegExp method is used to check regular expressions, while path-to-regexp can be regarded as a regular expression of url strings.

analysis

Applied in Bread Crumb components/Breadcrumb/index.vue,

Analyse the principle of this component:

  • Get and filter the matched attributes in the current routing to find the meta attributes that need to be displayed
  • When the click is triggered, the current route is obtained, the redirect attribute is judged, and if the value exists, the route is jammed in; otherwise, if there is params, the route will be supplemented completely.
import pathToRegexp from 'path-to-regexp'


   pathCompile(path) {
      // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
      const { params } = this.$route
      var toPath = pathToRegexp.compile(path)
      return toPath(params)
    },

vue-router Managing Routing

Vue Router is the official Vue.js routing manager. It is deeply integrated with the core of Vue.js, making it easy to build single-page applications.

Features

  • Nested Route/View Table
  • Modular Component-based Routing Configuration
  • Routing parameters, queries, wildcards
  • View Transition Effect Based on Vue.js Transition System
  • Fine-grained navigation control
  • Links to CSS class es with automatic activation
  • HTML5 history mode or hash mode, automatically downgraded in IE9
  • Customized scrollbar behavior

analysis

Integrated vue-router


import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

/* Layout */
import Layout from '@/layout'

Import pages to router/index.js in the routing center. Below is a section of intercepting routing-page mapping.

getNav is the way to get the template / page/NavPage/index path.

function getNav() {
  return () => import('@/page/NavPage/index')
}

......

{
  path: '/jobs',
  component: Layout,
  redirect: '/jobs/recruitmentPlatform',
  name: 'Jobs',
  meta: {
    title: 'work',
    icon: 'jobs'
  },
  children: [{
    path: 'recruitmentPlatform',
    name: 'RecruitmentPlatform',
    component: getNav(),
    meta: {
      title: 'work-Recruitment Platform',
      icon: 'recruitmentPlatform'
    }
  },
  {
    path: 'partTimeProgram',
    name: 'PartTimeProgram',
    component: getNav(),
    meta: {
      title: 'work-Procedure Part-time',
      icon: 'partTimeProgram'
    }
  },
  {
    path: 'partTimeDesign',
    name: 'PartTimeDesign',
    component: getNav(),
    meta: {
      title: 'work-Design Part-time',
      icon: 'partTimeDesign'
    }
  },
  {
    path: '/jobs/iframeNav',
    name: 'jobsIframeNav',
    hidden: true,
    component: () => import('@/page/iframeNav/index'),
    meta: {
      title: 'website',
      icon: 'iframeNav'
    }
  }
  ]
},


......

Using router to generate pages

const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({
    y: 0
  }),
  routes: constantRoutes
})

const router = createRouter()

// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}

export default router

See the template NavPage/index.vue code github warehouse

screenfull

A simple wrapper for cross-browser use of the JavaScript Fullscreen API that allows full-screen display of pages or any element.

vue-waterfall2 Construction of Waterfall Flow Layout

Waterfall adaptive plug-ins for vue and delay loading are very simple!

import waterfall from 'vue-waterfall2'
Vue.use(waterfall)
 <waterfall
        :col="col"
        :width="itemWidth"
        :gutter-width="gutterWidth"
        :data="navArr"
        @loadmore="loadmore"
        @scroll="scroll"
      >
        <template>
          <div v-for="(nav,key) in navArr" :key="key" style="margin-top: 10px;">
            <el-card :body-style="{ padding: '10px' }" shadow="hover">
              <img :src="nav.logo" class="image" alt="Loading error">
              <el-form label-width="100px" label-position="left">
                <el-form-item label="Website Name">
                  {{ nav.name }}
                </el-form-item>
                <el-form-item label="iframe link">
                  <router-link class="font-website" :to="{ path: 'iframeNav', query: { website: nav.website }}">
                    {{ nav.website }}
                  </router-link>
                </el-form-item>
                <el-form-item label="New window links">
                  <a class="font-website" :href="nav.website" target="_blank">{{ nav.website }}</a>
                </el-form-item>
                <el-form-item label="Website Description">
                  <div>{{ nav.describe || 'You need to add a website description' }}</div>
                </el-form-item>
              </el-form>
              <div class="bottom clearfix">
                <time class="time">Creation time:{{ nav.created_at|timeTrans }}</time>
                <el-button type="text" class="button" @click="openDialog(nav)">edit</el-button>
                <el-button type="text" class="button" @click="deleteMap(nav)">delete</el-button>
              </div>
            </el-card>
          </div>
        </template>
      </waterfall>

commitizen Submitting git normalization

commitizen command line utility.

When working in the Commitizen Friendly Repository, the system will prompt you to fill in the required fields and format the submission messages according to the criteria defined by the project maintainer.


// Global installation of commitizen node module
npm install commitizen -g 
// Initialize your project by typing the following command to use the cz-convention-change-elog adapter
commitizen init cz-conventional-changelog --save-dev --save-exact
type is used to specify the category of commit. Only the following seven identifiers are allowed
  • feat: new feature s
  • Fix: fix bug s
  • docs: documentation
  • style: Format (without affecting changes in code execution)
  • refactor: refactoring (i.e., not new functionality, nor code changes that modify bug s)
  • test: add tests
  • chore: Changes in the build process or ancillary tools

Scope is used to illustrate the scope of the impact of this Commit, that is, to outline the parts that will be involved in the revision, such as the data layer, the control layer, the view layer, and so on.

The location of subject comment, a brief description of this submission

iframe nested website

                <el-form-item label="iframe link">
                  <router-link class="font-website" :to="{ path: 'iframeNav', query: { website: nav.website }}">
                    {{ nav.website }}
                  </router-link>
                </el-form-item>
                <el-form-item label="New window links">
                  <a class="font-website" :href="nav.website" target="_blank">{{ nav.website }}</a>
                </el-form-item>

The iframe link in the page adds router-link to the iframe page, but the jumped link adds each classified route, so the iframe route is added to every classified route of the routing file.


  {
    path: '/iframeNav',
    name: 'frontIframeNav',
    hidden: true,
    component: () => import('@/views/iframeNav/index'),
    meta: {
      title: 'website',
      icon: 'iframeNav'
    }
  }

  {
    path: '/back-end/iframeNav',
    name: 'backIframeNav',
    hidden: true,
    component: () => import('@/views/iframeNav/index'),
    meta: {
      title: 'website',
      icon: 'iframeNav'
    }
  }

  ......

All clicks from the iframe link jump to this page

<template>
  <iframe ref="inlineFrameExample" title="Inline Frame Example" width="100%" height="898px" :src="iframeSrc" />
</template>
<script>
export default {
  data() {
    return {
      iframeSrc: ''
    }
  },
  created() {
    this.iframeSrc = this.$route.query.website
  }
}
</script>

Adaptation

According to the non-responsiveness of vue-waterfall2, the adaptation function can only be solved by the editor himself.

On the mobile side, set one column for him, three columns for the side bar, and four columns for the rest.

The width of each card is calculated by the width of the screen and the number of columns.

Give it a fixed value for the distance between cards. Note that when moving, you must set it to 0, otherwise the next columns will shift to the right.

computed: {
    col() {
      if (this.device === 'mobile') {
        return 1
      }
      if (this.sidebar.opened === true) {
        return 3
      }
      return 4
    },
    itemWidth() {
      if (this.device === 'mobile') {
        return (0.885 * (document.documentElement.clientWidth / 1))
      }
      if (this.sidebar.opened === true) {
        return (0.8 * (document.documentElement.clientWidth / 3))
      }
      return (0.9 * (document.documentElement.clientWidth / 4))
    },
    gutterWidth() {
      if (this.device === 'mobile') {
        return 0
      }
      return (9 * 0.5 * (document.documentElement.clientWidth / 375))
    },
    ...mapGetters([
      'sidebar',
      'device'
    ])
  },

Website Classification

Web site classification data comes from router, but router data must be filtered to get classification results.

The last three in the categoryOptions array are not categorized items, so they need to be removed.

/**
 * get categoryOptions from routes
 * @param {HTMLElement} routes
 * @param {HTMLElement} tag: text/label
 */
export function getOption(tag, routes) {
  let categoryOptions = []
  for (let i = 0; i < routes.length; i++) {
    if (routes[i].path !== '/redirect') {
      const children = routes[i].children
      for (const j in children) {
        const obj = {
          value: ''
        }
        obj.value = children[j].path
        obj[tag] = children[j].meta.title
        categoryOptions.push(obj)
      }
    }
  }
  categoryOptions = categoryOptions.filter(item => {
    return item.label !== 'website'
  })
  // Delete the last three elements
  return categoryOptions.slice(0, -3)
}

Then the template page calls this method

import {
  getOption
} from '@/utils/index'

this.categoryOptions = getOption('label', routes)

expectation

Next article, "Navigation submission tools for chrome development"

At present, the project has basically been completed, but there is still a lot of room for expansion. For example, submitting websites is more troublesome, at this time there is a chrome submission tool, all the problems will be solved.

In addition, this project will be maintained for a long time. I hope you can actively mention pr and issue, make this project more perfect, and help more people learn the practical application of vue in addition to official demo, avoid more pits.

Finally, don't forget to order a star for this project. Thank you for your support.

navigation-web front-end code repository

navigation-server back-end code repository

Here's the public number in the small edition.

A public number for learning programming techniques. Push high quality excellent blog posts, open source projects, practical tools, interview skills, programming learning resources and so on every day. The goal is to grow up with personal technology and public numbers. Welcome everybody to pay attention, make progress together and go to the road of cultivation of the whole stack elder man

Tags: node.js Mongoose Session Vue Database

Posted on Thu, 05 Sep 2019 00:56:06 -0700 by ari_aaron