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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354