前端工程化知识零散学习(面试篇)

require.context()

require.context()是一个webpack的api,主要作用是批量导入指定模块,解决了手动导入模块的繁琐。

用法:
通过执行require.context()函数获取一个特定的上下文(特定文件夹)。然后获取当前的文件,实现导入。

语法:require.context(String:directory,Boolean:useSubdirectories,regExp:regexp)

参数:
directory:String,读取文件的路径
useSubdirectories:Boolean,是否遍历文件子目录。
regexp:Regexp,匹配文件的正则表达

分析:
什么时候需要使用require.context()
如果有以下情况,可以使用。
盗图:
当导入模块时:

index.js

模块文件:


module.js

基于Vue-cli的一个自定义UI插件的小demo
文件结构:


文件结构

我的packages文件夹中有两个组件ButtonIcon,我需要将他们导入到index.js文件中然后注册给一个暴露了install方法的对象,之后就可以在 main.js中使用Vue.use()方法注册插件全局使用。
使用vue-cli3自定义插件并发布到npm参考我的文章:vue-cli3自定义插件并发布到npm

如果不使用require.context()方法时,是这样做的。
index.js

import Button from './Button.vue'; //导入Button组件
import Icon from './Icon.vue';//导入Icon组件
let MsComponent = {};
MsComponent.install = function(Vue,options){
    Vue.component('ms-button',Button); //全局注册Button组件
    Vue.component('ms-icon',Icon); //全局注册Icon组件
}

export default MsComponent

可以看到手动import每个需要的组件,然后分别调用Vue.component()去进行全局注册。
这样如果我的组件很多的情况下,逐个导入与注册就会让代码变得非常长。一点都不优雅。

使用require,context()

let MsComponent = {};
MsComponent.install = function(Vue,options){
    let components = require.context('./',true,/\.vue$/);//当前文件夹下以.vue结尾的所有文件
    console.log(components); // components
    components.keys().forEach((item)=>{
        let componentConfig = components(item)
        console.log(componentConfig) //componentConfig
        Vue.component(componentConfig.default.name,componentConfig.default)
    })

}
export default MsComponent
  1. 注释里面的输出 components 是一个方法


    components是一个方法
  2. 注释里面的输出 components.keys() 方法返回的是所有的组件路径list.


    所有的组件list
  3. 注释里面的输出 componentConfig 是一个对象里面包含了组件的信息。


    componentConfig组件的信息

比如default属性里面包含了组件的生命周期,name,render方法等。

源码 大概长这个样子:

var map = {
    "./Button.vue": "./src/packages/Button.vue",
    "./Icon.vue": "./src/packages/Icon.vue"
};


function webpackContext(req) {
    var id = webpackContextResolve(req);
    return __webpack_require__(id);
}
function webpackContextResolve(req) {
    if(!__webpack_require__.o(map, req)) {
        var e = new Error("Cannot find module '" + req + "'");
        e.code = 'MODULE_NOT_FOUND';
        throw e;
    }
    return map[req];
}
webpackContext.keys = function webpackContextKeys() {
    return Object.keys(map);
};
webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id = "./src/packages sync recursive \\.vue$";

参考文章:使用require.context实现前端工程自动化

babel工作原理

Babel本质就是操作AST来完成代码转译;
三个阶段:

  1. Parse(解析):将源代码转换成更加抽象的表示方法(AST抽象语法树)
  2. Transform(转换):对AST(抽象语法树)做一些特殊处理,使其更符合编译器的期望
  3. Generate(代码生成):将第二步经过转换的AST(抽象语法树)生成新的代码

Babel 是一个被广泛使用的代码转码器,可以将 ES6 代码转为 ES5 代码,这意味着你可以现在就用 ES6 编写程序,而不用担心现有环境是否支持 ES6。Babel 的工作原理就是先将 ES6 源码转换为 AST,然后再将 ES6 语法的 AST 转换为 ES5 语法的 AST,最后利用 ES5 的 AST 生成 JavaScript 源代码。

参考:
关于babel的详细解读(精华又通俗)
AST抽象语法树
初学 Babel 工作原理

网络请求Axios

Axios是一个支持node端和浏览器端,支持Promise,拥有丰富的配置项的网络请求工具。

安装:npm install axios

执行GET请求

axios.get("/user?ID=123")
  .then((response)=>{
     //捕获请求成功
  })
  .catch((error)=>{
    //捕获请求失败
  })

GET请求参数可以直接拼接在路径后面,也可以直接在第二个参数中传入一个对象,将参数放置在对象的params属性上

axios.get("/user",{
  params:{
    ID:123
  }
})
.then((response)=>{
   //捕获请求成功
})
.catch((error)=>{
  //捕获请求失败
})

执行POST请求

axios.post("/user",{
  name:"mstian",
  age:18
})
.then((response)=>{
  //捕获请求成功
})
.catch((error)=>{
  //捕获请求失败
})

POST请求参数为第二个参数是一个对象。

执行多个并发请求:

function getUserAccount(){
  return axios.get("/user")
}

function getUserPermission(){
  return axios.get("/user/permission")
}

axios.all([getUserAccount(),getUserPermission()])
.then(axios.spread((acct,perms)=>{
   //两个请求都成功
}))

Axios API
可以通过向axios传递相关配置来创建请求

axios({
  method:"post",
  url:"/user',
  data:{
    name:"mstian",
    age:18
  }
});

axios默认为get请求

axios("/user")
//默认发送get请求

为了方便所有请求方法都有别名

axios.request(config)
axios.get(url[,config])
axios.post(url[,config])
axios.put(url[,config])

创建实例
可以使用自定义配置新建一个axios实例
axios.create([config]);

var instance = axios.create({
  baseURL:"http://www.tianleilei.cn",
  timeout:1000
})

请求拦截

instance.interceptors.request.use((config)=>{
  //config请求体
  //可以在这里做一些请求前的配置,比如动态增加header
  config.headers.Authorization = "token"
  return config
},(err)=>{
   return Promise.reject(err)
})

响应拦截

instance.interceptors.response.use((response)=>{
  //对响应数据加工处理
  if(response.status>=200 && response.status<300 || response.status === 304){
    return Promise.resolve(response.data)
  }else{
    return Promise.reject(response)
  }
},(error)=>{
  console.log(error)
})

响应结构

{
  // `data` 由服务器提供的响应
  data: {},

  // `status` 来自服务器响应的 HTTP 状态码
  status: 200,

  // `statusText` 来自服务器响应的 HTTP 状态信息
  statusText: 'OK',

  // `headers` 服务器响应的头
  headers: {},

  // `config` 是为请求提供的配置信息
  config: {}
}

全局axios默认值

axios.defaults.baseURL = "https://www.api.example.com"
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

网络请求封装参考

import axios from 'axios'
import Qs from 'qs'
import store from '@/store'
import router from '@/router'
import Vue from 'vue'
import { Loading, Message } from 'element-ui' // 引用element-ui的加载和消息提示组件

const $axios = axios.create({
  // 设置超时时间
  timeout: 30000,
  // 基础url,会在请求url中自动添加前置链接
  baseURL: process.env.VUE_APP_BASE_API
})
Vue.prototype.$http = axios // 并发请求
// 在全局请求和响应拦截器中添加请求状态
let loading = null

// 请求拦截器
$axios.interceptors.request.use(
  config => {
    loading = Loading.service({ text: '拼命加载中' })
    const token = store.getters.token
    if (token) {
      config.headers.Authorization = token // 请求头部添加token
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)
// 响应拦截器
$axios.interceptors.response.use(
  response => {
    if (loading) {
      loading.close()
    }
    const code = response.status
    if ((code >= 200 && code < 300) || code === 304) {
      return Promise.resolve(response.data)
    } else {
      return Promise.reject(response)
    }
  },
  error => {
    if (loading) {
      loading.close()
    }
    console.log(error)
    if (error.response) {
      switch (error.response.status) {
        case 401:
          // 返回401 清除token信息并跳转到登陆页面
          store.commit('DEL_TOKEN')
          router.replace({
            path: '/login',
            query: {
              redirect: router.currentRoute.fullPath
            }
          })
          break
        case 404:
          Message.error('网络请求不存在')
          break
        default:
          Message.error(error.response.data.message)
      }
    } else {
      // 请求超时或者网络有问题
      if (error.message.includes('timeout')) {
        Message.error('请求超时!请检查网络是否正常')
      } else {
        Message.error('请求失败,请检查网络是否已连接')
      }
    }
    return Promise.reject(error)
  }
)

// get,post请求方法
export default {
  post(url, data) {
    return $axios({
      method: 'post',
      url,
      data: Qs.stringify(data),
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
      }
    })
  },
  get(url, params) {
    return $axios({
      method: 'get',
      url,
      params
    })
  }
}

参考:
https://www.kancloud.cn/yunye/axios/234845

node-glob模块

node-glob模块可以用来使用*等符号获取匹配对应规则的文件。
语法:glob(pattern,cb) pattern表示匹配文件的规则
安装:npm i glob
使用:

const glob = require('glob');
glob("**/*.js",function(err,files){
  console.log(files);
})

其中**/*,表示当前目录下所有文件夹中的js文件,src/*.js表示当前目录下src目录下的所有js文件,file/a[0-3].js表示当前目录下file目录下文件名为a0,a1,a2,a3的js文件。
files是一个数组,表示所获取到的文件名。比如[ 'file/a0.js', 'file/a1.js', 'file/a3.js' ]
这种文件操作都是异步操作,glob提供同步方法:glob.sync(pattern)

let files = glob.sync("file/*.js")
console.log(files); 
// [
  'file/a0.js',
  'file/a1.js',
  'file/a3.js',
  'file/a5.js',
  'file/img.js',
  'file/text.js'
] 

实际应用,如果需要获取文件名进行操作,比如webpack多页面打包需要手动增加htmlWebpackPlugin插件配置时就可以使用glob模块优雅高效的处理。https://www.jianshu.com/p/cc8eee4a430c

参考:
https://www.npmjs.com/package/glob
node-glob学习

未完待续。。。

写在最后:文中内容大多为自己平时从各种途径学习总结,文中参考文章大多收录在我的个人博客里,欢迎阅览http://www.tianleilei.cn

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。