웹 팩 의 기본 컴 파일 원 리 를 모방 하여 모듈 패키지 도 구 를 시험 적 으로 작성 합 니 다.

46561 단어 webpack
전체적인 사고방식:
  • 초기 화 매개 변수: 설정 파일 과 Shell 구문 에서 파 라 메 터 를 읽 고 통합 하여 최종 파 라 메 터 를 얻 을 수 있 습 니 다.
  • 컴 파일 시작: 이전 단계 에서 얻 은 매개 변수 로 Compiler 대상 을 초기 화하 고 모든 설정 플러그 인 을 불 러 옵 니 다. 실행 대상 의 run 방법 으로 컴 파일 을 시작 합 니 다.
  • 입 구 를 확정 합 니 다. 설정 중의 entry 에 따라 모든 입구 파일 을 찾 습 니 다.
  • 컴 파일 모듈: 입구 파일 에서 출발 하여 모든 설정 의 Loader 를 호출 하여 모듈 을 번역 한 다음 에 이 모듈 에 의존 하 는 모듈 을 찾 아 이 절 차 를 재 귀적 으로 모든 입구 에 의존 하 는 파일 이 처 리 될 때 까지 합 니 다.
  • 모듈 컴 파일 완료: 4 단 계 를 거 쳐 Loader 를 사용 하여 모든 모듈 을 번역 한 후에 각 모듈 이 번 역 된 최종 내용 과 그들의 의존 관 계 를 얻 었 습 니 다.
  • 출력 자원: 입구 와 모듈 간 의 의존 관계 에 따라 여러 모듈 을 포함 하 는 Chunk 로 조립 한 다음 에 모든 Chunk 를 하나의 단독 파일 로 변환 하여 출력 목록 에 추가 합 니 다. 이 단 계 는 출력 내용 을 수정 할 수 있 는 마지막 기회 입 니 다.
  • 출력 완료: 출력 내용 을 확인 한 후 설정 에 따라 출력 경로 와 파일 이름 을 확인 하고 파일 내용 을 파일 시스템 에 기록 합 니 다.이 과정 에서 웹 팩 은 특정 시점 에 특정 이 벤트 를 방송 하고 플러그 인 은 관심 있 는 이 벤트 를 감청 한 후 특정 논 리 를 수행 하 며 플러그 인 은 웹 팩 이 제공 하 는 API 를 호출 하여 웹 팩 의 실행 결 과 를 변경 할 수 있 습 니 다.

  • 웹 팩 의 hooks
  • entryOption 설정 파일 읽 기
  • after Plugins 모든 플러그 인 불 러 오기
  • run 컴 파일 프로 세 스 실행 시작
  • 컴 파일 시작
  • afterCompile 컴 파일 완료
  • emit 파일 쓰기
  • done 전체 절차 완성
  • \#\# 구체 적 인 실현 절차:
  • 1 두 개의 디 렉 터 리 sourcepack (자체 포장 도구 디 렉 터 리 실현) 와 usepack (아 날로 그 프로젝트 디 렉 터 리) 를 새로 만 듭 니 다.
  • usepack
    ├── src                      #     
    │   ├── a                    #     
    │   ├── loaders              #   loadder  
    │   ├── plugins              #   plugin  
    │   ├── index.js             #     
    │   ├── index.less           # less  
    ├── webpack.config.json      # webpack     
    ├── package.json             #     
    

    1. webpack 설정 webapck. config. js 를 작성 하면 다음 과 같 습 니 다.
    
    const path = require("path");
    const entryOptionPlugin = require("./src/plugins/entry-option-plugin");
    
    module.exports = {
    	entry:"./src/index.js",
    	mode:"development",
    	output:{
    		path:path.resolve("dist"),
    		filename:"bundle.js"
    	},
    	resolveLoader:{
    		modules:'./src/loaders'
    	},
    	module:{
    		rules:[{
    			test:/\.less$/,
    			loader:['style-loader','less-loader']
    		}]
    	},
    	plugins:[
    		new entryOptionPlugin()
    	]
    }
    
    

    2, 입구 파일 index. js
        let a1 = require("./a/a1");
        require('./index.less');
        alert("sourcePack");
    

    3, a 디 렉 터 리 아래 a1. js
        const a2 = require('./a2.js');
        module.exports = a2;
    

    a 디 렉 터 리 아래 a2. js
       module.exports = "this is a2";
    

    4. plugins 디 렉 터 리 아래 entry - option - plugin. js
    
    class entryOptionPlugin {
    	constructor(options){
    
    	}
    	apply(compiler){
    		compiler.hooks.entryOption.tap('entryOptionPlugin',function(options){
    			console.log("      ...")
    		});
    	}
    }
    
    module.exports = entryOptionPlugin;
    
    

    5, loaders 디 렉 터 리 아래 less - loader. js
        let less = require("less");
        module.exports = function(source){
            let css;
            less.render(source,(error,output)=>{
                css = output.css;
            });
            return css.replace(/
    /g,'\
    '); }

    loaders 디 렉 터 리 아래 style - loader. js
      module.exports = function(source){
    	let style = `
    		let style = document.createElement('style');
    		style.innerHTML = ${JSON.stringify(source)};
    		document.head.appendChild(style);
    	`;
    	return style;
    }
    
    
    sourcepack
    ├── bin                      #      
    │   ├── sourcepack.js        #    
    ├── lib                      #      
    │   ├── compiler.js          # compiler  
    │   ├── main.ejs             # ejs   
    ├── package.json             #     
    

    1, package. json 에 빈 필드 추가
      "bin": {
        "sourcepack": "./bin/sourcepack.js"
      },
    

    2, 실행 npm link 소프트 연결 구축
    3,bin/sourcepack.js
    
    const path = require("path");
    const fs = require("fs");
    const root = process.cwd();
    const configPath = path.join(root,"webpack.config.js");
    const config = require(configPath);
    const Compiler = require('../lib/Compiler');
    const compiler = new Compiler(config);
    //   entryOption   
    compiler.hooks.entryOption.call(config);
    compiler.run();
    
    

    4,lib/compiler.js
    
    const { SyncHook }  = require("tapable");
    const path = require("path");
    const fs = require("fs");
    //  AST 
    const esprima = require("esprima");
    //     
    const estraverse = require("estraverse");
    //     
    const escodegen = require("escodegen");
    
    const ejs = require("ejs");
    
    class Compiler{
    	constructor(options){
    		//        
    		this.root = process.cwd();
    		//         moduleId =>    
    		this.modules = {};
    		this.options = options;
    		this.hooks = {
    			entryOption:new SyncHook(['config']),
    			afterPlugins:new SyncHook(['afterPlugins']),
    			run:new SyncHook(['run']),
    			compile:new SyncHook(['compile']),
    			afterCompile:new SyncHook(['afterCompile']),
    			emit:new SyncHook(['emit']),
    			done:new SyncHook(['done'])
    		}
    		let plugins = options.plugins;
    		if(plugins&&plugins.length>0)
    			plugins.forEach(plugin=>{
    				plugin.apply(this);
    			})
    		//          
    		this.hooks.afterPlugins.call(this);
    	}
    	//            
    	run(){
    		const { 
    			entry, 
    			output:{ path: pathName, filename }
    		}= this.options;
    		let _this = this;
    		const entryPath = path.join(this.root,entry);
    
    		this.hooks.compile.call(this);
    		this.parseModule(entryPath,true);
    		this.hooks.afterCompile.call(this);
    
    		let bundle = ejs.compile(fs.readFileSync(path.join(__dirname,'main.ejs'),"utf8"))({
    			modules:this.modules,entryId:this.entryId
    		});
    
    		this.hooks.emit.call(this);
    
    		fs.writeFileSync(path.join(pathName,filename),bundle);
    
    		this.hooks.done.call(this);
            	
    	}
    
    	parseModule(modulePath,isEntry){
    		const { 
    			module: { rules } ,
    			resolveLoader:{ modules: loaderPath }
    		}= this.options;
    		//         
    		let source = fs.readFileSync(modulePath,'utf8');
    
    		for (var i =0;i < rules.length; i++) {
    			let rule = rules[i];
    			if(rule.test.test(modulePath)){
    				let loaders = rule.use||rule.loader;
    				if( Object.prototype.toString.call(loaders)==='[object Array]'){
    					
    					for(let j = loaders.length-1;j>=0;j--){
    						let loader = loaders[j];
    						loader = require(path.join(this.root,loaderPath,loader));
    						source = loader(source);
    					}
    
    				}else if( Object.prototype.toString.call(loaders)=== "[object Object]"){
    					loaders  = loader.loader;
    				}
    			}
    		}
    		let parentPath = path.relative(this.root,modulePath);
    		//TODO   loader      
    		let result = this.parse(source,path.dirname(parentPath));//                 
    
    		this.modules['./'+parentPath] = result.source;
    		if(isEntry) { this.entryId = './'+parentPath };
    
            let requires = result.requires;
            if( requires && requires.length>0){
            	requires.forEach(function(req){
            		this.parseModule(path.join(this.root,req));
            	}.bind(this))
            }
    	}
    	//         。1.             2,      
    	parse(source,parentPath){ // parentPath      
    		//  AST
    		let ast = esprima.parse(source);
    		//         
    		const requires = [];
    		//     。1.           2,          
    		estraverse.replace(ast,{
    			enter(node,parent){
    				if(node.type == "CallExpression" && node.callee.name == "require"){
    					let name = node.arguments[0].value;
    					name += (name.lastIndexOf('.')>0?"":".js");
    				    let moduleId = "./"+path.join(parentPath,name);
    				    requires.push(moduleId);
    				    node.arguments= [{type:"Literal",value:moduleId}];
    				    //          
    				    return node; 
    				}
    			}
    		});
    		source = escodegen.generate(ast);
    		return { requires, source };
    	}
    }
    
    module.exports = Compiler;
    
    

    5,lib/main/ejs
    /******/ (function(modules) { // webpackBootstrap
    /******/ 	// The module cache
    /******/ 	var installedModules = {};
    /******/
    /******/ 	// The require function
    /******/ 	function __webpack_require__(moduleId) {
    /******/
    /******/ 		// Check if module is in cache
    /******/ 		if(installedModules[moduleId]) {
    /******/ 			return installedModules[moduleId].exports;
    /******/ 		}
    /******/ 		// Create a new module (and put it into the cache)
    /******/ 		var module = installedModules[moduleId] = {
    /******/ 			i: moduleId,
    /******/ 			l: false,
    /******/ 			exports: {}
    /******/ 		};
    /******/
    /******/ 		// Execute the module function
    /******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    /******/
    /******/ 		// Flag the module as loaded
    /******/ 		module.l = true;
    /******/
    /******/ 		// Return the exports of the module
    /******/ 		return module.exports;
    /******/ 	}
    /******/
    /******/
    /******/ 	// expose the modules object (__webpack_modules__)
    /******/ 	__webpack_require__.m = modules;
    /******/
    /******/ 	// expose the module cache
    /******/ 	__webpack_require__.c = installedModules;
    /******/
    /******/ 	// define getter function for harmony exports
    /******/ 	__webpack_require__.d = function(exports, name, getter) {
    /******/ 		if(!__webpack_require__.o(exports, name)) {
    /******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
    /******/ 		}
    /******/ 	};
    /******/
    /******/ 	// define __esModule on exports
    /******/ 	__webpack_require__.r = function(exports) {
    /******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
    /******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
    /******/ 		}
    /******/ 		Object.defineProperty(exports, '__esModule', { value: true });
    /******/ 	};
    /******/
    /******/ 	// create a fake namespace object
    /******/ 	// mode & 1: value is a module id, require it
    /******/ 	// mode & 2: merge all properties of value into the ns
    /******/ 	// mode & 4: return value when already ns object
    /******/ 	// mode & 8|1: behave like require
    /******/ 	__webpack_require__.t = function(value, mode) {
    /******/ 		if(mode & 1) value = __webpack_require__(value);
    /******/ 		if(mode & 8) return value;
    /******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
    /******/ 		var ns = Object.create(null);
    /******/ 		__webpack_require__.r(ns);
    /******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
    /******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
    /******/ 		return ns;
    /******/ 	};
    /******/
    /******/ 	// getDefaultExport function for compatibility with non-harmony modules
    /******/ 	__webpack_require__.n = function(module) {
    /******/ 		var getter = module && module.__esModule ?
    /******/ 			function getDefault() { return module['default']; } :
    /******/ 			function getModuleExports() { return module; };
    /******/ 		__webpack_require__.d(getter, 'a', getter);
    /******/ 		return getter;
    /******/ 	};
    /******/
    /******/ 	// Object.prototype.hasOwnProperty.call
    /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
    /******/
    /******/ 	// __webpack_public_path__
    /******/ 	__webpack_require__.p = "";
    /******/
    /******/
    /******/ 	// Load entry module and return exports
    /******/ 	return __webpack_require__(__webpack_require__.s = "");
    /******/ })
    /************************************************************************/
    /******/ ({
    	
    			/***/ "":
    			/***/ (function(module, exports, __webpack_require__) {
    
    			eval(``);
    
    			/***/ }),
    	
    
    /******/ });
    
  • 두 디 렉 터 리 가 해당 하 는 의존 도 를 다운로드 한 후 usewebpack 디 렉 터 리 에서 sourcepack 명령 을 실행 합 니 다
  • dist 디 렉 터 리 에 index. html
  • 을 새로 만 듭 니 다.
    
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<title>Documenttitle>
    head>
    <body>
    	<h1>sourcePackh1>
    	<script src="./bundle.js">script>
    body>
    html>
    
    
  • 클릭 하여 소스 코드 보기
  • 좋은 웹페이지 즐겨찾기