CSS 패키지 플러그인 스타일의 현대 네트워크에서 무슨 일이 일어났는지

​현대 응용 프로그램의 스타일을 디자인하는 것은 결코 간단한 작업이 아니다. 전통적으로 HTML과 CSS를 제공하여 스타일을 디자인하고 자바스크립트를 추가하여 작업을 완성한다.
당신은 어떻게 이런 응용 프로그램을 설치하는 방법을 현대화합니까?우리는 Webpack과 같은 묶음 프로그램과 React와 같은 자바스크립트 프레임워크/라이브러리를 사용한다고 생각할 수 있습니다.
그러나 CSS 는 어떻게 처리합니까? 왜 예상한 대로 간단하지 않습니까?
의사일정 -
  • 섹션 1: 네이티브 CSS 문제 이해
  • 섹션 2: CSS 플러그인 없이 웹 응용 프로그램을 설정합니다.
  • 섹션 3: 로더 작성
  • 섹션 4: 고급 플러그인 작성
  • 만약 당신이 이곳에서 단지 실현 정보를 이해하기 위해서라면 3부분으로 넘어가세요.

    면책 성명 - 이것은 생산에 사용할 수 있는 플러그인이 아니다.이 점을 알고 싶으면 저와 제 팀이 진행 중인 프로젝트 Stylable을 보십시오.

    섹션 1: 기본 CSS 문제를 이해합니다.

    우리의 선택
    네이티브 CSS는 다음과 같은 다양한 방식으로 구현됩니다.
  • 에 CSS를 포함하는 첫 번째 방법은 인라인 스타일을 사용하는 것입니다. 이것은 HTML 태그에 현시적으로 스타일을 포함한다는 것을 의미합니다.<span style="color:red;">...</span>
  • 의 또 다른 해결 방안은 <style>...</style>이라는 HTML 태그를 사용하는데 그 중의 텍스트 내용은 스타일 자체로 서로 다른 HTML 요소에 사용된다.
  • , 링크 태그를 통해 CSS 파일을 로드하고 해당 파일의 다른 HTML 요소에 대한 옵션도 있습니다.

  • 문제.
    상술한 모든 해결 방안은 장점과 이해득실을 가지고 있다.그것들을 이해하는 것은 스타일링에서 의외의 행동을 피하는 데 매우 중요하다.그러나 이 해결 방안들이 가장 문제가 되는 문제 중 하나인 CSS가 전 세계적이라는 것을 알게 될 것이다.
    전 세계 문제는 극복하기 어려운 문제다.예를 들어, 당신은 버튼이 하나 있는데, 그 위에 btn이라는 종류가 있는데, 당신은 그것을 설계할 수 있습니다.어느 날, 당신의 동료가 다른 단추가 있는 페이지에서 일했는데, 그도 btn이라고 부르기로 결정했다.문제는 뻔할 것이다. 스타일이 충돌할 수 있다는 것이다.
    또 다른 중요한 문제는 선택기 간의 특이성이 같고 CSS의 마지막 성명은 요소에 적용된다는 것이다.간단히 말하면, 당신의 주문서는 매우 중요합니다.

    섹션 2: CSS 플러그인 없이 Webpack 응용 프로그램을 설정합니다.

    솔루션
    현재 이러한 문제에 대해 많은 다른 해결 방안이 있는데 실용 프로그램 프레임워크, CSS 프로세서, 그리고 원생 CSS 문제를 해결하는 데 도움을 주려고 하는 다른 방법들이 있다.
    본문에서 나는 모두와 함께 그 중의 일부 문제를 처음부터 해결하고 싶다.
    우선 환경을 신속하게 설치합시다.이렇게 하려면 다음 명령을 실행합니다.
    (우리는 디렉터리를 만들고 패키지.json을 초기화하며 WebpackBabel 의존항을 설치합니다.)
    mkdir example-css-plugin
    cd example-css-plugin
    npm init -y
    npm i -D webpack webpack-cli @webpack-cli/generators @babel/preset-react
    npm i react react-dom
    
    개발 종속성을 설치한 후 Webpack init 명령을 실행합니다.
    npx webpack init
    
    우리의 설정에 대한 당신의 답은 다음과 같아야 합니다.
    ? Which of the following JS solutions do you want to use? ES6
    ? Do you want to use webpack-dev-server? Yes
    ? Do you want to simplify the creation of HTML files for your bundle? Yes
    ? Do you want to add PWA support? No
    ? Which of the following CSS solutions do you want to use? none
    ? Do you like to install prettier to format generated configuration? No
    

    Make sure that this question is answered as such: "Which of the following CSS solutions do you want to use" - none.



    구성 반응
    사전 설정 어레이가 포함되어 있는지 확인하려면 .babelrc으로 이동하십시오.
    이것은 반드시 필요한 것은 아니지만, 우리의 프로젝트가 jsx을 바꿀 수 있도록 확보하기 위해서이다.
    {
        "plugins": ["@babel/syntax-dynamic-import"],
        "presets": [
            [
                "@babel/preset-env",
                {
                    "modules": false
                }
            ],
                "@babel/preset-react"
        ]
    }
    
    지금 우리는 색인에 들어가야 한다.html, 그리고 id가 "루트"인div가 있는지 확인하십시오.
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8" />
            <title>CSS Webpack Plugin example</title>
        </head>
        <body>
            <div id="root"></div>
        </body>    
    </html>
    
    그런 다음 src/index.js에서 애플리케이션을 작성할 예정입니다.
    import React from 'react';
    import { render } from "react-dom";
    
    render(
      <div>
        Hello World!
      </div>,
      document.getElementById('root')
    )
    

    섹션 3: 로더 작성
    그럼 저희 목표는 뭐예요?우선, 우리는 JS에서 CSS를 불러오기만 원합니다.
    CSS 파일을 만들고 이름을 index.css으로 지정합니다.
    .app {
        background: red;
    }
    
    물론 index.js 파일에서는 다음을 사용합니다.
    import React from 'react';
    import { render } from 'react-dom';
    import './index.css'
    
    render(
      <div className="app"> Hello World! </div>,
      document.getElementById('root')
    );
    
    애플리케이션을 실행하는 방법:
    npm run serve
    
    이제 콘솔에서 다음 오류가 발생할 수 있습니다.

    이 오류는 매우 일리가 있습니다. 왜냐하면 Webpack은 CSS 가져오기를 어떻게 처리하는지 모르기 때문입니다. 우리는 그것을 어떻게 처리하는지 알려야 합니다.

    웹 패키지 로더 만들기


    무엇이 적재기입니까?
    Webpack에서는 로더를 사용하여 파일을 미리 처리할 수 있습니다.이렇게 하면 JavaScript 이외의 방법으로 정적 리소스를 번들로 묶을 수 있습니다.
    간단하게 말하자면, 우리의 예에서, 그것들은 CSS 파일을 js 파일을 입력하고 출력하는 함수로 한다.
    CSS->JS


    캐리어 구현webpack.config.js이라는 loader.js 옆에 파일을 만듭니다.
    CSS 파일에서 얻은 스타일 값을 dom에 추가하는 것이 목표입니다.loader.js :
    // Appending the style inside the head
    function appendStyle(value) {
        const style = document.createElement('style');
        style.textContent = value;
        document.head.appendChild(style);
    }
    
    // Make sure it is not an arrow function since we will need the `this` context of webpack
    function loader(fileValue) {
      // We stringify the appendStyle method and creating a file that will be invoked with the css file value in the runtime
      return `
        (${appendStyle.toString()})(${JSON.stringify(fileValue)})
      `
    }
    
    module.exports = loader;
    
    
    현재 우리는 웹 페이지 설정에 그것을 등록해야 한다.webpack.config.js :
    const config = {
      //... rest of the config
        module: {
            rules: [
              // ... other rules not related to CSS
                {
                    test: /\.css$/,
                    loader: require.resolve('./loader')
                }
            ]
        }
      // ...
    }
    
    터미널을 다시 시작합니다. 성공했습니다.🎊


    막후에 무슨 일이 일어났습니까?
    Webpack은 index.js에서 CSS 가져오기를 볼 수 있습니다.그것은 로더를 찾고 우리가 실행할 때 계산하고자 하는 자바스크립트 값을 제공합니다.

    글로벌 문제 극복
    이제 우리는 자신의 풍격이 생겼지만, 모든 것이 전 세계적이다.다른 모든 언어는 역할 영역이나 명칭 공간을 통해 전 세계 문제를 해결한다.물론 CSS 자체는 not a programming language이지만 이 논점은 여전히 성립된다.
    우리는 명칭 공간 해결 방안을 실현할 것이다.이것은 모든 파일에 자신의 이름 공간이 있는 범위를 제공할 것입니다.
    예를 들어, 가져오기는 다음과 같습니다.
    AppComponent123__myClass
    
    만약 다른 구성 요소가 같은 클래스 이름을 가지고 있다면, 막후에서는 중요하지 않다. 왜냐하면 이름 공간이 다르기 때문이다.loader.js으로 이동하여 다음 방법을 추가합니다.
    const crypto = require('crypto');
    
    /**
     * The name is the class we are going to scope, and the file path is the value we are going to use for namespacing.
     * 
     * The third argument is the classes, a map that points the old name to the new one.
     */
    function scope(name, filepath, classes) {
      name = name.slice(1); // Remove the dot from the name.
      const hash = crypto.createHash('sha1'); // Use sha1 algorithm.
      hash.write(filepath); // Hash the filepath.
    
      const namespace = hash.digest('hex').slice(0, 6); // Get the hashed filepath.
      const newName = `s${namespace}__${name}`;
    
      classes[name] = newName; // Save the old and the new classes.
    
      return `.${newName}`
    }
    
    클래스의 역할 영역을 완성한 후 로더 방법을 되돌려줍니다.
    역할 영역 선택기를 사용자의 자바스크립트 코드에 연결하는 방법이 필요합니다.
    function loader(fileValue) {
      const classes = {}; // Map that points the old name to the new one.
      const classRegex = /(\.([a-zA-Z_-]{1}[\w-_]+))/g; // Naive regex to match everything that starts with a dot.
      const scopedFileValue = fileValue.replace(classRegex, (name) => scope(name, this.resourcePath, classes)); // Replace the old class with the new one and add it to the classes object
    
     // Change the fileValue to scopedFileValue and export the classes.
      return `
        (${appendStyle.toString()})(${JSON.stringify(scopedFileValue)})
    ​
        export default ${JSON.stringify(classes)}
      ` // Export allows the user to use it in their javascript code
    }
    
    이제 index.js에서 객체로 사용할 수 있습니다.
    import React from 'react';
    import { render } from "react-dom";
    import classes from './index.css'; // Import the classes object.
    
    render(
      <div className={classes.app /* Use the app class  */}>
        Hello World
      </div>,
      document.getElementById('root')
    )
    
    현재 이름 공간 선택기와 함께 사용할 수 있습니다🎉
    네임스페이스 선택기를 사용하여 초기화
    우리가 실시한 변혁에 관한 몇 가지 요점
  • Webpack이 마운트 프로그램을 사용할 때 상하문은 Webpack의 마운트 프로그램 상하문(this)이 됩니다.그것에 대한 더 많은 정보를 읽을 수 있습니다. here.이것은 해석된 파일 경로를 제공하여 이름 공간이 파일에 유일한 것을 제공합니다.
  • 우리가 CSS 파일에서 클래스 선택기를 추출하는 방법은 유치한 실현이고 다른 용례를 고려하지 않았다.CSS 파서를 사용하는 것이 좋습니다.
  • this.resourcePath은 로컬 경로를 가리키며 다른 기계에서는 경로가 달라 보일 수 있음을 의미한다.

    캐리어는 이미 실현되었고, 현재 우리는 이미 역할 영역류가 있다.그러나 모든 컨텐트가 JavaScript에서 로드되므로 CSS를 캐시할 수는 없습니다.

    이를 위해서는 모든 CSS를 하나의 파일로 조합해야 하며, 이를 위해서는 패키지 플러그인을 만들어야 한다.


    섹션 4: 고급 플러그인 작성

    앞에서 말한 바와 같이, 우리는 CSS를 우리의 페이지에 주입할 수 있는 마운트 프로그램을 실현했다.그러나 만약 우리가 주입이 아닌 신호 파일을 사용하고 싶다면?

    CSS를 파일로 로드하면 캐시가 가장 좋습니다.브라우저는 필요할 때마다 다시 다운로드하지 않고 이 파일을 캐시할 수 있다.

    이 조작은loader 사례보다 더 복잡합니다. 왜냐하면 우리는 Webpack 귀속 과정에서 더 많은 상하문을 가지고 있기 때문입니다.

  • 플러그인이란 무엇입니까?

    Webpack 플러그인은 apply 메서드가 있는 JavaScript 객체입니다.이 apply 방법은 Webpack 컴파일러가 호출하여 전체 컴파일러 생명주기에 접근할 수 있도록 합니다.


    플러그인 만들기
    plugin.js이라는 파일을 만들고 플러그인 프레임워크를 만듭니다.
    
    class CSSPlugin {
      cssMap = new Map() // We will save the CSS content here
    
      /**
       * Hook into the compiler
       * @param {import('webpack').Compiler} compiler 
       */
      apply(compiler) { }
    }
    
    module.exports = {
      CSSPlugin
    }
    
    
    이제 apply 방법을 실현해 봅시다.
    
    class CSSPlugin {
      cssMap = new Map() // We will save the CSS content here
    
      /**
       * Hook into the compiler
       * @param {import('webpack').Compiler} compiler 
       */
      apply(compiler) {
    
        // Hook into the global compilation.
        compiler.hooks.thisCompilation.tap('CSSPlugin', (compilation) => {
    
          // Hook into the loader to save the CSS content.
          compiler.webpack.NormalModule.getCompilationHooks(compilation).loader.tap(
            'CSSPlugin',
            (context, module) => {
    
              // Setting up a method on the loader context that we will use inside the loader.
              context.setOutputCSS = (css) => {
    
                // the key is the resource path, and the CSS is the actual content.
                this.cssMap.set(module.resource, css)
              }
            }
          )
        })
       }
    }
    
    우리는 전역 컴파일러에 연결한 다음 마운트 프로그램에 연결합니다. (이전에 이미 이루어졌습니다.)

    로더 내용에 접근할 수 있을 때 set OutputCSS 방법을 추가해서 로더에서 호출합니다.
    loader.js에서 이 메서드를 사용하는 방법은 다음과 같습니다.
    function loader(fileValue) {
      const classes = {}; // Map that points the old name to the new one.
      const classRegex = /(\.([a-zA-Z_-]{1}[\w-_]+))/g; // Naive regex to match everything that starts with a dot.
      const scopedFileValue = fileValue.replace(classRegex, (name) => scope(name, this.resourcePath, classes)); // Replace the old class with the new one and add it to the classes object
    
      this.setOutputCSS(scopedFileValue) // Pass the scoped CSS output
    
     // Export the classes.
      return `export default ${JSON.stringify(classes)}`
    }
    
    보시다시피 JavaScript에 스타일을 추가하지 않았습니다.우리는 상하문에 추가하는 방법을 사용한다.

    모든 한정된 범위의 CSS 내용을 수집한 후에 우리는 현재 자산 프로세스 갈고리를 걸어 컴파일러가 우리가 새로운 자산을 처리해야 한다는 것을 알게 해야 한다.

    Apply 메서드에 추가합니다.
    class CSSPlugin {
      // ...
    
      apply(compiler) {
          compiler.hooks.thisCompilation.tap(
            'CSSPlugin', 
            (compilation) => {
            // ...
    
            // Hook into the process assets hook
            compilation.hooks.processAssets.tap(
              {
                name: 'CSSPlugin',
                stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED
              },
              () => {
    
                    // Loop over the CSS content and add it to the content variable
                    let content = '';
                    for (const [path, css] of this.cssMap) {
                      content += `/* ${path} */\n${css}\n`;
                    }
    
                    // Append the asset to the entries.
                    for (const [name, entry] of compilation.entrypoints) {
                      assetName = `${name}.css`;
                      entry.getEntrypointChunk().files.add(assetName);
                    }
    
                    // Create the source instance with the content.
                    const asset = new compiler.webpack.sources.RawSource(content, false);
    
                    // Add it to the compilation
                    compilation.emitAsset(assetName, asset);
              }
          )
      }
    }
    

    이제 build 명령을 실행합니다.
    npm run build
    
    우리는 메인을 보러 가야 한다.폴더의 css를 내보내고 HTML에 주입하려면 다음과 같이 하십시오.
    출력:
    index.html :

    이렇게!
    플러그인을 완성하고 모든 CSS에 대한 CSS 파일을 만들었습니다.

    데모를 위해 종속 항목, 그래픽 정렬 및 사용되지 않는 CSS 필터링을 건너뛰었습니다.

    이 리포 here에서 제 typescript와 테스트의 완전한 실현을 볼 수 있습니다.

    만약 당신에게 무슨 문제가 있으면 전화로 저에게 연락할 수 있습니다.나는 내가 너를 도울 수 있기를 바란다.

    좋은 웹페이지 즐겨찾기