DayDayUp Learning List

website·Indexed 30 days ago

https://epeejs.github.io/daydayup

A comprehensive collection of frontend development guides and technical notes covering webpack optimization, data structures (binary trees, linked lists, sorting), Babel plugin implementation, Koa middleware, React Router internals, and automated testing.

Tokens
10.3K
Snippets
42
Records
53
Agent score
75%

What's inside DayDayUp

  1. Understand Block Formatting Context (BFC) behavior

    A Block Formatting Context (BFC) creates an isolated layout environment. Elements inside a BFC are positioned independently, ensuring that internal child elements do not affect the layout of elements outside the BFC. Key behaviors include:

    • Containing internal floats: The BFC will expand to contain floated children.
    • Excluding external floats: The BFC will not overlap with floated elements outside of it.
    • Preventing margin collapse: Margins of elements inside a BFC do not collapse with margins of elements outside the BFC.
  2. React Router v5 routing mechanism overview

    In React Router v5, the routing flow works as follows: when a route change occurs, the history object creates a new location object. The Router component listens for events triggered by history, updates the location value within the RouterContext, which in turn triggers the Switch component to match the current path against defined Route components and render the corresponding UI.
  3. Distinguish between Compiler and Compilation

    In Webpack's architecture:

    • Compiler: Represents the entire lifecycle of Webpack from startup to shutdown.
    • Compilation: Represents a single compilation instance created by the Compiler for each build. It contains the current module resources, generated assets, and a list of changed files.
  4. Implement Hot Module Replacement (HMR) in Webpack

    Webpack HMR works by injecting client-side code into the browser via webpack-dev-server. When code changes, a new patch file is compiled and sent to the browser. Update events bubble up the module tree; if a module accepts the change, a callback is executed. If the event reaches the top without being accepted, the entire page refreshes.
    if (module.hot) {
      module.hot.accept(['./App'], () => {
        render(<App />, window.document.getElementById('app'));
      });
    }
  5. Understand the Webpack build process

    The Webpack build process follows these sequential steps:

    1. Initialize parameters.
    2. Initialize the Compiler object using parameters, load all configured plugins, and execute the run method to start compilation.
    3. Recursively find all dependencies based on the entry point and process files using configured loaders based on file type.
    4. Construct a dependency graph after all module transformations are complete.
    5. Assemble the dependency graph into chunks containing multiple modules and add these chunks to the output list (this is the final stage where plugins can modify output content).
    6. Determine the output paths and filenames based on configuration and write them to the file system.
  6. Reduce webpack bundle size using on-demand imports

    To improve build speed, avoid importing entire libraries. Instead, import only the specific modules or components needed. For libraries like lodash, antd, and echarts, you can import from their internal paths. Alternatively, use Babel plugins to automate this transformation while keeping the standard import syntax.
    // lodash: Import specific function
    import _map from 'lodash/map';
    _map(users, 'user');
    
    // antd: Import specific component and its style
    import Button from 'antd/lib/button';
    import 'antd/lib/button/style/css';
    
    // echarts: Import core and required components
    import echarts from 'echarts/lib/echarts';
    import 'echarts/lib/component/tooltip';
    import 'echarts/lib/component/title';
    import 'echarts/lib/chart/line';

    Automation Plugins:

    • lodash: babel-plugin-lodash
    • antd: babel-plugin-import
  7. Implement routing using URL Hash fragments

    Hash-based routing uses location.hash to manage the application state. Changes to the hash can be detected by listening to the onhashchange event. This method does not require special server-side configuration because the hash portion of the URL is not sent to the server.
    window.addEventListener('onhashchange', function(e) {
      // Handle route change
    });
    
    function push(path) {
      location.hash = path;
    }
    
    function replace(path) {
      const hashIndex = location.href.indexOf('#');
      const href = hashIndex === -1 ? url : url.slice(0, hashIndex);
      location.replace(href + '#' + path);
    }
  8. Implement a request caching mechanism using MapCache and apiHelper

    To prevent duplicate API requests within PaaS components or route middleware, you can implement a caching layer using a MapCache class to store responses and an apiHelper decorator to wrap request methods. The system supports time-based cache expiration and merges concurrent identical requests into a single Promise.
    // 1. Define the Cache Structure
    export class MapCache {
      map = new Map();
    
      static getKey(parmas) {
        return JSON.stringify(parmas);
      }
    
      get(key) {
        return this.map.get(key) && this.map.get(key).value;
      }
      set(key, value) {
        return this.map.set(key, { value, timestamp: Date.now() });
      }
      // Check if cache is hit based on maxTime
      hit(key, maxTime) {
        const { timestamp = 0 } = this.map.get(key) || {};
        return (Date.now() - timestamp < maxTime);
      }
    }
    
    // 2. Create the Decorator to extend request methods
    export function apiHelper(fn) {
      const apiCache = new MapCache();
    
      return function (...args) {
        let cache = false;
    
        // Check if caching is enabled (passed as the last argument)
        if (args.length === 2) {
          cache = args.splice(-1)[0];
        }
        if (!cache) {
          return fn.apply(this, args);
        }
    
        // Handle cache configuration
        const { maxTime } = typeof cache === 'boolean' ? { maxTime: 1000 } : cache;
        const { url, params, data, method } = args[0];
        const key = MapCache.getKey({ url, params, data, method });
    
        if (apiCache.hit(key, maxTime)) {
          return apiCache.get(key);
        }
    
        const response = fn.apply(this, args);
        // Store the response (supports merging concurrent requests into one Promise)
        apiCache.set(key, response);
    
        return response;
      };
    }
    
    // 3. Usage Example with Axios
    import Axios from 'axios';
    const request = apiHelper(Axios.request);
    
    // Request with caching enabled (default 1000ms)
    request({ url: 'http://api.twitter.com', params: { a: 1 } }, true);
  9. Automate Taro page HOC wrapping via Babel plugin

    To avoid manually wrapping every new page with an HOC, you can implement a Babel plugin that automatically transforms the default export of page components. The process involves three steps: 1. Extracting the list of page paths from app.config.ts, 2. Identifying if the current file is a page component based on those paths, and 3. Transforming the ExportDefaultDeclaration to wrap the component with the HOC.
    // Step 1: Extract page paths from app.config.ts
    traverse(ast, {
      ArrayExpression(path) {
        if (
          t.isObjectProperty(path.parent) &&
          t.isIdentifier(path.parent.key) &&
          path.parent.key.name === 'pages'
        ) {
          pages = pages.concat(path.node.elements.map((m: any) => m.value));
        }
      },
    });
    
    // Step 2 & 3: Identify page and transform export
    {
      ExportDefaultDeclaration(path, state) {
        const hocSource = 'src/component/hoc';
        const hocName = '__hoc__';
        const filename = state.filename;
        const isPage = filename.includes('.config.')
          ? false
          : pages.some((m) => state.filename.includes(m));
    
        if (!t.isClassDeclaration(path.node.declaration) && isPage) {
          // Insert import statement
          path.insertBefore(
            t.importDeclaration(
              [t.importDefaultSpecifier(t.identifier(hocName))],
              t.stringLiteral(hocSource),
            ),
          );
          // Wrap export with HOC
          path.node.declaration = t.callExpression(t.identifier(hocName), [
            path.node.declaration as any,
          ]);
        }
      },
    }
  10. Implement asynchronous page interception in Taro H5 using an HOC

    In Taro, you cannot intercept child element rendering from the entry component (app.tsx) because Taro follows the mini-program design. To perform asynchronous operations (like SSO or pre-checks) before a page renders, you must wrap the page component in a Higher-Order Component (HOC).
    function hoc(Component) {
      return (props) => {
        const [loading, setLoading] = useState(false);
    
        useEffect(() => {
          const ticket = getQuery('ticket');
    
          if (ticket) {
            setLoading(true);
            // Simulate async process
            setTimeout(() => {
              setLoading(false);
            }, 2000);
          }
        }, []);
    
        return loading ? <div>loading</div> : <Component {...props} />;
      };
    }
    
    export default hoc;
    
    // Wrap the page component
    export default hoc(Home);
  11. Use DllPlugin to precompile common dependencies

    Use DllPlugin to precompile stable third-party libraries (vendor modules) into a separate bundle. This prevents webpack from re-bundling these libraries on every build. The process involves creating a DLL config, generating a manifest file, and referencing that manifest in the main webpack config.
    // 1. config/webpack.config.dll.js
    const path = require('path');
    const webpack = require('webpack');
    
    module.exports = {
      entry: {
        vendor: ['react', 'react-dom', 'redux', 'axios', 'antd'] // Add common libs here
      },
      output: {
        path: path.join(__dirname, '../public/static/js'),
        filename: '[name].dll.js',
        library: '[name]_library'
      },
      plugins: [
        // Ignore moment.js locale files to reduce size
        new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
        new webpack.DllPlugin({
          path: path.join(__dirname, '[name]-manifest.json'),
          name: '[name]_library'
        }),
        new webpack.optimize.UglifyJsPlugin({ parallel: true })
      ]
    };
    
    // 2. Main webpack.config.js
    module.exports = {
      plugins: [
        new webpack.DllReferencePlugin({
          manifest: require('./vendor-manifest.json')
        })
      ]
    };

    Implementation Steps:

    1. Add script to package.json: "build:dll": "webpack --config config/webpack.config.dll.js -p".
    2. Run npm run build:dll to generate the .dll.js bundle and .json manifest.
    3. Add the script tag to index.html: <script src="%PUBLIC_URL%/static/js/vendor.dll.js"></script>.
  12. Organize frontend test files and directories

    Follow these naming and directory conventions to maintain a clean test structure:

    • Unit Tests: Place in a __tests__ folder within the corresponding feature directory. Filenames should include .test.
    • E2E Tests: Place in a root-level e2e directory. Filenames should end in .test.ts.