Front end learning: vue3.0+tsx Netease cloud mobile imitation (some problems encountered and solutions)

Netease cloud mobile imitation

summary

It is mainly used to sort out the problems encountered in the development process and the development of modules that need to be used in my development process. If I think it is important, I will record it!

Technology used

front end

  • vue3.0+ts
  • rem layout better adapts to different mobile terminals
  • scss
  • swiper.js home page rotation + partial content sliding + search page tab

back-end

node.js

thinking

  • Use vue3.0+ts for development; cli is installed in the first section of the module; Basics
  • Selecting rem layout is conducive to the adaptation of various mobile terminals; Basics
  • To realize the home page rotation, you need to use swiper.js. At present, npm can be used to install it
  • Realize the popular data display + docking interface on the home page; complete
  • Realize the display of song list interface data; complete
  • Implement global player; complete
  • Realize the rotation of the disc playing the page, and the lyrics roll according to the time; complete
  • Implement the home page search page component; complete
  • Realize the specific search page content (multi content switching); complete
  • Set the content on the left side of the home page; complete
  • Realize personal center style; complete
  • Realize user login and registration; complete
  • Realize daily recommendation; complete
  • View other people's information; Not started
  • Achieve private FM; Not started
  • Realize the song list classification interface; complete
  • Realize the ranking page; Under development
  • Realize the song comment interface; Not started
  • Realize the relevant interfaces of yunbei; Not started
  • Wait to think

Module implementation

The implementation of some modules or components is mainly shown here;

Configuration required for vue3.0 CLI installation;

  • Install the latest version of vue cli

    npm install -g @vue/cli
    # OR
    yarn global add @vue/cli
    
  • Create a project using cli

    vue create appName
    
  • Please pick a preset will pop up. If it has not been installed before, select Manually select features and press enter; Because we need to set the configuration of a certain department!

  • Now prompt: Check the features needed for your project. Here, press the space to select or cancel after switching between the up and down keys. We need to select more here;

    • Choose Vue version select version
    • Bable compiles ES6 into ES5
    • TyperScript JS superset, mainly type checking
    • Router routing
    • Vuex status management
    • CSS pre processors CSS precompiling (this will be configured later)
    • Liner / Formatter code checking tool

    There are * signs here. Press enter to enter the next step

  • Enter after selecting 3.x

  • Enter use class style component syntax? Enter Y here

    Is a Class style decorator used?
    That is, it was originally: home = new Vue() to create Vue instances
    After using the decorator: class home extensions vue{}

  • Enter use Babel along side typescript (required for modern mode, auto detected polyfills, transmitting JSX)? Enter Y here

  • Enter Use history mode for router? (y/n is OK) I enter n

  • Go to pick a CSS pre processor to see if you are used to choosing sass / SCSS (with node SASS)

  • Enter Pick a linter / formatter config and select ESLint with error prevention only

  • Enter Pick additional lint features and select Lint on save as the code checking method

  • Go to Where do you prefer placing config for... And select In package.json

  • Enter Save this as a preset for future projects? Do you want to use the above configuration in future projects? You can save or not save. This is to skip the selection directly next time (I choose no n)

  • Start to install the setup (if it is created for the first time, you will choose to use npm or yarn for installation)

  • After installation

    cd xxxx
    npm run serve #The project is ready to run
    

Configure rem layout configuration

  • First, install the corresponding dependencies;

    npm install amfe-flexible --save
    npm install postcss-px2rem --save
    
  • Introduced in main.ts

    import "amfe-flexible";
    
  • Configure px2rem loader in package.json

    "postcss": {
        "plugins": {
          "autoprefixer": {},
          "postcss-px2rem": {
            "remUnit": 37.5 //This refers to the width of the design draft (370 / 10)
          }
        }
      },
    
    
  • Just restart the project after configuration. px will be converted to rem during use. If there is no vscode plug-in to change the format, you can use uppercase px to keep it from being converted;

  • If you use vscode and need rem prompt, you need to install a plug-in cssrem. The usage of this plug-in is not introduced here

How to implement global components

Many times, we use a component in many places. We need to import the component every time. At this time, we need to set the corresponding global component for us to call when necessary!

  • First, we are ready to define a type withInstall in utils/types.ts

    type withInstall<T> = T & { install(app: App): void };
    export { withInstall };
    
  • After writing the component, you need to create an index.ts file in the component directory to export the component, such as our input component

    input.jsx component file, index.scss style file,

    import { App } from "vue";
    import { withInstall } from "../utils/types";
    import MInput from "./input";
    
    MInput.install = (app: App) => {
      app.component(MInput.name, MInput);
    };
    export default MInput as withInstall<typeof MInput>;
    
  • Create the index.ts file in the location of our components, such as / src/components

    import { App } from "vue";
    import MInput from "./input/index"; //Location of custom components
    
    const components = [MInput]; //Multiple components can be
    
    export { MInput }; //You can export a single
    
    // Register components
    export default function(app: App) {
      components.forEach((item) => app.component(item.name, item));
    }
    
  • Introduce / src/components/index.ts in mian.ts

    import { createApp } from "vue";
    import App from "./App.vue";
    import { MInput } from "./components";
    
    createApp(App)
      .use(MInput)
      .mount("#app");
    
  • In this way, the global registration is successful, but I use this method when writing the ui component library. You can add the components you want to become global components at any time to facilitate registration!

Component through method call

The method call here is the same as that of the ElementUI

this.$comfirm(message, title, { xxx: xxx }); //Use the confirm method consistently

Warm note: I write the confirm component according to the writing method of vant's dialog component

  • First, create our confirm component. I won't explain the code too much below. We need to introduce another pop component we created for use in combination. Please see the code here. It's a little messy and there are no complete semi-finished products

    import { defineComponent, PropType, CSSProperties, ref } from "vue";
    import Popup from "../Popup/Popup";
    import "./index.scss";
    import { ClickEventFuncType } from "@/utils/types";
    
    export type ConfirmAction = "confirm" | "cancel";
    export type ConfirmMessage = string | (() => JSX.Element);
    export type ConfirmMessageAlign = "left" | "center" | "right";
    
    export default defineComponent({
      name: "Confirm",
      props: {
        callback: Function as PropType<(action?: ConfirmAction) => void>,
        modelValue: {
          type: Boolean,
          default: false,
        },
        title: String,
        width: [Number, String],
        message: [String, Function] as PropType<ConfirmMessage>,
        messageAlign: String as PropType<ConfirmMessageAlign>,
      },
      emits: ["confirm", "cancel", "update:modelValue"],
      setup(props, { emit, slots }) {
        const updateShow = (value: boolean) => emit("update:modelValue", value);
    
        const close = (action: ConfirmAction) => {
          updateShow(false);
          if (props.callback) {
            props.callback(action);
          }
        };
    
        const getActionHandler = (action: ConfirmAction) => () => {
          // should not trigger close event when hidden
          if (!props.modelValue) {
            return;
          }
          emit(action);
          close(action);
        };
    
        const onCancel = getActionHandler("cancel");
        const onConfirm = getActionHandler("confirm");
    
        const popStyle = ref<CSSProperties>({
          width: "6.5rem",
          borderRadius: "0.35rem",
          overflow: "hidden",
          backgroundColor: "#F1F0F0",
          textAlign: "center",
        });
    
        return () => {
          const { modelValue, title, message } = props;
          return (
            <div>
              <Popup
                clickModalClose={false}
                showSafeArea={false}
                visible={modelValue}
                direction="center"
                style={popStyle.value}
                {...{ "onUpdate:modelValue": updateShow }}
                v-slots={{
                  head: () => null,
                }}
              >
                <div class="comfirm">
                  <div class="comfirm-title">{title}</div>
                  <div class="comfirm-message">{message}</div>
                  <div class="comfirm-action">
                    <div class="comfirm-action-cancle" onClick={onCancel}>
                      cancel
                    </div>
                    <div class="comfirm-action-comfirm" onClick={onConfirm}>
                      determine
                    </div>
                  </div>
                </div>
              </Popup>
            </div>
          );
        };
      },
    });
    
  • Step 2: create another file confirm-function-call.tsx. Some things here were originally defined under until / index.ts, but in order to facilitate the definition of all things in this file at this time, you can pull them out later

    • First, introduce relevant modules

      import {
        App,
        Component,
        createApp,
        reactive,
        nextTick,
        getCurrentInstance,
        ComponentPublicInstance,
      } from "vue";
      import MConfirm, {
        ConfirmAction,
        ConfirmMessage,
        ConfirmMessageAlign,
      } from "./Confirm"; //Here is what the component needs
      
    • Define related functions and types

      export const extend = Object.assign;
      export const inBrowser = typeof window !== "undefined";
      const camelizeRE = /-(\w)/g;
      export function camelize(str: string): string {
        return str.replace(camelizeRE, (_, c) => c.toUpperCase());
      }
      export type ComponentInstance = ComponentPublicInstance<{}, any>;
      export type Interceptor = (...args: any[]) => Promise<boolean> | boolean;
      export type WithInstall<T> = T & {
        install(app: App): void;
      } & EventShim;
      //Register components
      export function withInstall<T>(options: T) {
        (options as Record<string, unknown>).install = (app: App) => {
          const { name } = options as any;
          app.component(name, options);
          app.component(camelize(`-${name}`), options);
        };
      
        return options as WithInstall<T>;
      }
      
      //Although the code is written here, I don't really understand it
      export function useExpose<T = Record<string, any>>(apis: T) {
        const instance = getCurrentInstance(); //Get component instance
        if (instance) {
          extend(instance.proxy, apis);
        }
      }
      
      // In fact, pop is defined in vant, but I changed it to my confirm
      // One thing to note here is that this modelValue is v-model when we use it as a component
      export function usePopupState() {
        const state = reactive<{
          modelValue: boolean;
          [key: string]: any;
        }>({
          modelValue: false,
        });
      
        const toggle = (modelValue: boolean) => {
          state.modelValue = modelValue;
        };
      
        const open = (props: Record<string, any>) => {
          extend(state, props);
          nextTick(() => toggle(true));
        };
      
        const close = () => toggle(false);
      
        useExpose({ open, close, toggle });
      
        return {
          open,
          close,
          state,
          toggle,
        };
      }
      
      // This is where you mount the component. You can see the code clearly
      export function mountComponent(RootComponent: Component) {
        const app = createApp(RootComponent);
        const root = document.createElement("div");
      
        document.body.appendChild(root);
      
        return {
          instance: app.mount(root),
          unmount() {
            app.unmount();
            document.body.removeChild(root);
          },
        };
      }
      
    • Specific operations on components

      export type ConfirmOptions = {
        title?: string;
        width?: string | number;
        message?: ConfirmMessage;
        beforeClose?: Interceptor;
        teleport?: string;
        messageAlign?: ConfirmMessageAlign;
        cancelButtonText?: string;
        showCancelButton?: boolean;
        showConfirmButton?: boolean;
        cancelButtonColor?: string;
        confirmButtonText?: string;
        confirmButtonColor?: string;
      };
      
      let instance: ComponentInstance;
      
      function initInstance() {
        const Wrapper = {
          setup() {
            const { state, toggle } = usePopupState();
            return () => (
              <MConfirm {...state} {...{ "onUpdate:modelValue": toggle }} />
            );
          },
        };
      
        ({ instance } = mountComponent(Wrapper));
      }
      
      function Confirm(options: ConfirmOptions) {
        /* istanbul ignore if */
        if (!inBrowser) {
          return Promise.resolve();
        }
      
        return new Promise((resolve, reject) => {
          if (!instance) {
            initInstance();
          }
      
          instance.open(
            extend({}, Confirm.currentOptions, options, {
              callback: (action: ConfirmAction) => {
                (action === "confirm" ? resolve : reject)(action);
              },
            })
          );
        });
      }
      
      Confirm.defaultOptions = {
        title: "",
        width: "",
        message: "",
        callback: null,
        teleport: "body",
        beforeClose: null,
        messageAlign: "",
        cancelButtonText: "",
        cancelButtonColor: null,
        confirmButtonText: "",
        confirmButtonColor: null,
        showConfirmButton: true,
        showCancelButton: false,
      };
      
      Confirm.currentOptions = extend({}, Confirm.defaultOptions);
      Confirm.alert = Confirm;
      
      Confirm.confirm = (options: ConfirmOptions) =>
        Confirm(extend({ showCancelButton: true }, options));
      
      Confirm.close = () => {
        if (instance) {
          instance.toggle(false);
        }
      };
      
      Confirm.setDefaultOptions = (options: ConfirmOptions) => {
        extend(Confirm.currentOptions, options);
      };
      
      Confirm.resetDefaultOptions = () => {
        Confirm.currentOptions = extend({}, Confirm.defaultOptions);
      };
      
      Confirm.Component = withInstall(MConfirm);
      
      Confirm.install = (app: App) => {
        app.use(Confirm.Component);
        app.config.globalProperties.$confirm = Confirm;
      };
      
      export { Confirm };
      
  • The above code can be used, but it is not well understood. It is very simple to use

    import { Confirm } from "@/components/Confirm";
    const MConfirm = Confirm.Component; //This can be used according to the component method
    //Used in tsx: < mconfirm > < / mconfirm >
    //Function use
    Confirm.confirm({
      title: "dadsa",
      message: "dddd",
    })
      .then((res) => {
        console.log("Click OK");
      })
      .catch(() => {
        console.log("Click Cancel");
      });
    

The underline below the navigation bar follows

  • We use the simplest method, but it can't have the effect of scrolling

    The code is also very simple. Add some new content under each navigation. Note that you need to set relative positioning

    div {
      position: relative;
      &::after {
        content: "";
        position: absolute;
        bottom: 0;
        left: 100%;
        width: 0;
        height: 2px;
        background-color: #000;
        transition: all 0.3s ease-out;
      }
    }
    &-active {
      color: #000;
      font-weight: 600;
      &::after {
        width: 100%;
        left: 0;
      }
    
      & + .nav::after {
        left: 0;
      }
    }
    
  • Set the underline to absolute positioning, and position the width through js

    Set scss first

    &-underline {
       position: absolute;
       bottom: 0;
       display: block;
       height: 4px;
       border-radius: 4px;
       background-color: red;
       transition: all 0.3s ease-out;
       margin-bottom: 0.1rem;
    }
    

    Then control the width and the distance to the left through js

    //Gets the current navigation itself
    const navActive: any = document.querySelector(".msearch-nav-active");
    //The distance passed to the left of the width of the function element
    moveUnderLine(navActive.offsetWidth, navActive.offsetLeft);
    //Define the moveUnderLine function for multiple uses
    const moveUnderLine = (width: number | string, left: number | string) => {
      navUnderlinStyle.value = {
        width: width + "px",
        left: left + "px",
      };
    };
    
  • Because our navigation itself also belongs to the scrolling attribute, and it will be displayed in the middle when scrolling is set, which is misplaced with the absolute positioning of our underline!

    At this time, underline and navigation need to be placed in the same div, and the positioning attribute of div is set to relative, so that the navigation bar can follow the scrolling position of navigation; The following shows the js code for automatic centering of navigation scrolling

    //Get active navigation element
    const navActive: any = document.querySelector(".msearch-nav-active");
    //Get the width of the entire scrolling area (set the width as much as possible)
    const navWidth = navBoxRef.value.offsetWidth;
    if (navActive) {
      //Gets the distance to the left of the active navigation
      const navOffsetWidth = navActive.offsetLeft;
      //The middle value is the offset width minus the width of the element itself and divided by 2
      const diffWidth = (navWidth - navActive.offsetWidth) / 2;
      //Distance to scroll targetWidth
      const targetWidth = navOffsetWidth - diffWidth;
      //Set scroll distance
      navBoxRef.value.scrollLeft = targetWidth;
      //The underline above follows the scroll
      moveUnderLine(navActive.offsetWidth, navOffsetWidth);
    }
    

vue3.0 transition animation switching

There is a disadvantage here. I don't know how to get the from and to of the route. I can only get some information of the route

design sketch

OK, to start, we need to define the attributes, and define an index in each route to represent our level

OK, to start, we need to define the attributes, and define an index in each route to represent our level

{
    path: "/",
    name: "Home",
    component: () => import("../views/home/index.vue"),
    meta: {
      keepAlive: true, //Need cache
      title: "Dream back to cloud music-home page",
      index: 1,
    },
  },
  {
    path: "/sheetList",
    name: "sheetList",
    component: () => import("../views/musicSheet/index.vue"),
    meta: {
      title: "Dream back to cloud music-song sheet",
      index: 2,
    },
  },

Then define the use transition in App.vue. See the code below

<router-view v-slot="{ Component }">
    <!-- vue3.0 to configure keep-alive cache-->
    <transition :name="$route.meta.transitionName">
      <keep-alive>
        <component
          :is="Component"
          v-if="$route.meta.keepAlive"
        />
      </keep-alive>
    </transition>
    <transition :name="$route.meta.transitionName">
      <component
        :is="Component"
        v-if="!$route.meta.keepAlive"
      />
    </transition>
  </router-view>

Here is a key step to monitor the changes of our route, but I can't get the current level and the level to go, so I can only use the method of vue2.x temporarily, and don't write it in the setup

watch: {
    $route(to, from) {
      //Prevents the animation effect from being triggered when the current page is refreshed
      if (from.meta.index === undefined) {
        to.meta.transitionName = "";
        return;
      }
      if (to.meta.index > from.meta.index) {
        to.meta.transitionName = "jump";
      } else {
        to.meta.transitionName = "back";
      }
    },
  },

We will define our css transition code animation style later

.back-enter-active,
.back-leave-active,
.jump-enter-active,
.jump-leave-active {
  will-change: transform;
  transition: all 0.5s;
  width: 100%;
  position: absolute;
  z-index: 99;
}
.jump-enter-from {
  opacity: 0;
  transform: translate3d(100%, 0, 0);
}
.jump-leave-active {
  opacity: 0;
  transform: translate3d(-100%, 0, 0);
}
.back-enter-from {
  opacity: 0;
  transform: translate3d(-100%, 0, 0);
}
.back-leave-active {
  opacity: 0;
  transform: translate3d(+100%, 0, 0);
}

This method can be well displayed in click return and event return, but when we use sliding return or forward, the animation will be executed twice. What I want is to cancel the animation effect by judging the direction of the gesture (not yet written)

I hope someone will tell me how to quickly know what it returns or advances through.

Realize night mode

Effect achieved:

  • First define our themes theme file -

    //themes.scss
    $themes: (
      light: (
        //Background color
          background_color: #f8f8f8,
        //The nature of the main text,,,,,
          text-color: #000,
        //Secondary background color
          secondary-bg: #fff,
      ),
      dark: (
        //background
          background_color: #080808,
        //The nature of the main text,,,
          text-color: #f8f8f8,
        //Secondary background color
          secondary-bg: #1a1919,
      ),
    );
    
  • Define operation file

    //handle.scss
    //Traverse topic map
    @mixin themeify {
      @each $theme-name, $theme-map in $themes {
        //! Global strongly promotes local variables to global variables
        $theme-map: $theme-map !global;
        //Judge that the attribute value #{} of html data theme is the interpolation expression of sass
        //&The parent container ID @ content in sass nest is the mixer slot, like the slot of vue
        [data-theme="#{$theme-name}"] & {
          @content;
        }
      }
    }
    //Declare a function to get color according to Key
    @function themed($key) {
      @return map-get($theme-map, $key);
    }
    //Get background color
    @mixin background_color($color) {
      @include themeify {
        background: themed($color) !important;
      }
    }
    //Get font color
    @mixin font_color($color) {
      @include themeify {
        color: themed($color) !important;
      }
    }
    
  • The last is the usage method, which needs to introduce scss

    //example.scss
    @import "@/assets/css/handle.scss"; //Your own path
    * {
      //You can introduce your other designed colors in brackets
      @include background_color("background_color");
      @include font_color("text-color");
    }
    

problem

This is mainly used for some problems and solutions I encounter in the process of writing code

####After using fixed, slide the fixed page and the bottom layer will scroll

tps: this method can only be used for mobile terminals

When onTouchMove is set for the modal layer, the default behavior is blocked. The code is as follows:

//Define events
const preventTouchMove = (event: TouchEvent) => {
  if (props.lockScroll) {
    event.preventDefault();
  }
};
//Use in tsx
<div
  class="mh-modal"
  style={{
    zIndex: props.mIndex,
  }}
  v-show={props.show}
  onClick={handleClick}
  onTouchmove={preventTouchMove}
></div>;

However, when I still have internal rolling, sometimes it will drive the bottom rolling. It has not been solved yet. I hope the boss can help provide a good solution

position:sticky layout failure in CSS

  • The subject will not move the corresponding content upward, but it will be affected by the parent overflow. Overflow cannot be set

  • Effective only within the parent element,

  • The height of the parent element cannot be lower than the height of the sticky element

  • You must specify one of the four values top, bottom, left and right, otherwise it will only be in relative positioning

.xxx {
  position: sticky;
  top: 0;
  z-index: 100;
}

Warning when introducing swiper 6.0 and using it in tsx

Warning Code:

lot "default" invoked outside of the render function: this will not track dependencies used in the slot. Invoke the slot function inside the render function instead.

I've been looking for a long time, but I haven't found a specific solution, but the code runs normally,

If you don't want to see this code, you can set the code warning to turn off vue (Baidu by yourself), do not use tsx syntax for development, or don't trigger this prompt,

If there is a better solution, please let us know

The use of Transition in vue3 and tsx syntax has no animation effect

In tsx syntax, Transition must be introduced before use,

import { Transition } from "vue";

It should be noted that in tsx syntax, if you control the state of display by yourself, you cannot trigger the Transition animation of Transition;

In tsx syntax, we can use v-show, but not v-if. Otherwise, an error will be reported. V-show can trigger transition animation

In fact, v-if itself is also a ternary operator, a very simple operation

But as for why I dispaly can't trigger the animation effect, I'm helpless

When you use ref to get the value, you will be prompted that the object may be null

Because sometimes when we use the ref of vue3.0 to create variables, we will create variables like the following

const xxxref = ref(null);

Then bind to dom

<div ref={NavRef}></div>

However, when we use it, tslint will prompt that the object may be null

Because we didn't define the corresponding type judgment, so we can write it another way to give it a type

const xxxref = ref<any>(null); //or
const xxxref = ref();

In fact, like the two lines of code, ref will infer the type by itself. Of course, you have special values that can be defined before use

How to use audio tag (rarely used before)

Because this program is a music player, you need to use the audio tag to play music

<audio
  src={`https://music.163.com/song/media/outer/url?id=.mp3`}
  ref={playControRef}
  autoplay={playing.value ? true : false}
  onEnded={playEnd}
  onPlay={onPlay}
  onPause={onPause}
  onTimeupdate={onTimeupdate}
></audio>
  • Autoplay autoplay accept Boolean
  • onPlay play
  • onPause pause
  • onEnded playback ended
  • onTimeupDate the playback time is updated, which is similar to the setInterval loop
  • ... (wait for update, drag progress bar, etc.)

When defining click,touchmove and other functions, you need to use the problems encountered when using event

When this problem is not a component, using these methods directly on the component will allow you to define your own emit before using it, otherwise an error will be reported

When we want to organize default events or use functions to pass parameters, we cannot directly pass parameters in vue3+tsx. We need to declare the corresponding types, as shown below

  • When passing an index in tsx syntax

    <div
      class={[
        "read-play-music-left",
        index === props.currentIndex ? " active" : "",
      ]}
      onClick={clickHandler(index)}
    ></div>
    
  • When accepted in a function, the type must be defined before the corresponding value can be used

    type ClickHandler = (index: number) => (e: MouseEvent) => void;
    const clickHandler: ClickHandler = (index) => (e) => {
      e.preventDefault();
      store.commit("setPlayCurrntIndex", index);
    };
    

It is forbidden to zoom in the h5 interface, and the models above + iosX are adapted to the bottom safety zone and other settings

  • First, add related configurations in the html interface meta

    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
    />
    

    User scalable = no disable scaling

    Viewport fit = cover is used for safe zone adaptation

  • Add in the css code that needs to configure security adaptation

    padding-bottom: constant(safe-area-inset-bottom); ///Compatible with IOS < 11.2/
    padding-bottom: env(safe-area-inset-bottom); ///Compatible with IOS > 11.2/
    

    It should be noted that this method is only suitable for content positioned at the bottom

Use swiper.js ` if the height of each slider is not fixed, each slider will scroll synchronously

You need to set the height of each content. You cannot scroll according to the height of the slider itself

Prompt error when using localStorage.getItem in ts

Error prompt: [external chain image transfer failed, and the source station may have anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-a1duwuk5-16324676993) (C: \ users \ 12743 \ appdata \ roaming \ typora \ typora user images \ image-20210905003941646. PNG)]

state.historySearch = JSON.parse(localStorage.getItem("historySearch"));

terms of settlement:

state.historySearch = JSON.parse(localStorage.getItem("historySearch") || "[]");

Error reason

Prompt: do not use '{}' or '[]', because JSON.parse('{}') and JSON.parse('[]') are true!!

Vue3.0 subcomponent changes props through update

In the vue2.x version, you can change the value through the parent component visible.sync = "xxxxx" and then the child component update:xxx

sync has been discarded in vue3.0. You need to use the value bound by v-model to modify it through update:xxx

When tsx is writing code, the data of its own pit (self built pit) subcomponent cannot respond

Don't write the defined ref data in the return of setup. The sub components can't work normally. Let's see the error code here

setup() {
    //It should be here
    return () => {
        //Remember not to write here!!!!!
      const show = ref(false);
      const percentage = ref(10);
      const changeShow = () => {
        show.value = !show.value;
      };
      const format = (percentage: number | string) => {
        return Number(percentage) === 100 ? "full" : `${percentage}%`;
      };
      return (
        <div>
          <Progress
            percentage={percentage.value}
            showText={show.value}
            format={format}
          />
          <hr />
          <input
            type="range"
            max="100"
            min="0"
            v-model={percentage.value}
            step="1"
          />

          <div onClick={changeShow}>Let me try</div>
        </div>
      );
    };
  },

summary

  • I'm not very proficient in vue3.x API. This is because I started to write a demo after reading some tutorials. However, in the process of writing this demo, I encountered problems and solved them. This kind of learning method is also a very good way. Because of the foundation of vue2.x, most of them are not affected.
  • They are not familiar with ts syntax, and their understanding of ts is limited to the limitation of types, but some of the high-level syntax will not be used. Of course, they can only understand the writing of high-level syntax when they see other people's code, so as to learn the knowledge points;

Warehouse address

Netease cloud mobile imitation

summary

It is mainly used to sort out the problems encountered in the development process and the development of modules that need to be used in my development process. If I think it is important, I will record it!

Technology used

front end

  • vue3.0+ts
  • rem layout better adapts to different mobile terminals
  • scss
  • swiper.js home page rotation + partial content sliding + search page tab

back-end

node.js

thinking

  • Use vue3.0+ts for development; cli is installed in the first section of the module; Basics
  • Selecting rem layout is conducive to the adaptation of various mobile terminals; Basics
  • To realize the home page rotation, you need to use swiper.js. At present, npm can be used to install it
  • Realize the popular data display + docking interface on the home page; complete
  • Realize the display of song list interface data; complete
  • Implement global player; complete
  • Realize the rotation of the disc playing the page, and the lyrics roll according to the time; complete
  • Implement the home page search page component; complete
  • Realize the specific search page content (multi content switching); complete
  • Set the content on the left side of the home page; complete
  • Realize personal center style; complete
  • Realize user login and registration; complete
  • Realize daily recommendation; complete
  • View other people's information; Not started
  • Achieve private FM; Not started
  • Realize the song list classification interface; Under development
  • Realize the ranking page; Not started
  • Realize the song comment interface; Not started
  • Realize the relevant interfaces of yunbei; Not started
  • Wait to think

Module implementation

The implementation of some modules or components is mainly shown here;

Configuration required for vue3.0 CLI installation;

  • Install the latest version of vue cli

    npm install -g @vue/cli
    # OR
    yarn global add @vue/cli
    
  • Create a project using cli

    vue create appName
    
  • Please pick a preset will pop up. If it has not been installed before, select Manually select features and press enter; Because we need to set the configuration of a certain department!

  • Now prompt: Check the features needed for your project. Here, press the space to select or cancel after switching between the up and down keys. We need to select more here;

    • Choose Vue version select version
    • Bable compiles ES6 into ES5
    • TyperScript JS superset, mainly type checking
    • Router routing
    • Vuex status management
    • CSS pre processors CSS precompiling (this will be configured later)
    • Liner / Formatter code checking tool

    There are * signs here. Press enter to enter the next step

  • Enter after selecting 3.x

  • Enter use class style component syntax? Enter Y here

    Is a Class style decorator used?
    That is, it was originally: home = new Vue() to create Vue instances
    After using the decorator: class home extensions vue{}

  • Enter use Babel along side typescript (required for modern mode, auto detected polyfills, transmitting JSX)? Enter Y here

  • Enter Use history mode for router? (y/n is OK) I enter n

  • Go to pick a CSS pre processor to see if you are used to choosing sass / SCSS (with node SASS)

  • Enter Pick a linter / formatter config and select ESLint with error prevention only

  • Enter Pick additional lint features and select Lint on save as the code checking method

  • Go to Where do you prefer placing config for... And select In package.json

  • Enter Save this as a preset for future projects? Do you want to use the above configuration in future projects? You can save or not save. This is to skip the selection directly next time (I choose no n)

  • Start to install the setup (if it is created for the first time, you will choose to use npm or yarn for installation)

  • After installation

    cd xxxx
    npm run serve #The project is ready to run
    

Configure rem layout configuration

  • First, install the corresponding dependencies;

    npm install amfe-flexible --save
    npm install postcss-px2rem --save
    
  • Introduced in main.ts

    import "amfe-flexible";
    
  • Configure px2rem loader in package.json

    "postcss": {
        "plugins": {
          "autoprefixer": {},
          "postcss-px2rem": {
            "remUnit": 37.5 //This refers to the width of the design draft (370 / 10)
          }
        }
      },
    
    
  • Just restart the project after configuration. px will be converted to rem during use. If there is no vscode plug-in to change the format, you can use uppercase px to keep it from being converted;

  • If you use vscode and need rem prompt, you need to install a plug-in cssrem. The usage of this plug-in is not introduced here

How to implement global components

Many times, we use a component in many places. We need to import the component every time. At this time, we need to set the corresponding global component for us to call when necessary!

  • First, we are ready to define a type withInstall in utils/types.ts

    type withInstall<T> = T & { install(app: App): void };
    export { withInstall };
    
  • After writing the component, you need to create an index.ts file in the component directory to export the component, such as our input component

    input.jsx component file, index.scss style file,

    import { App } from "vue";
    import { withInstall } from "../utils/types";
    import MInput from "./input";
    
    MInput.install = (app: App) => {
      app.component(MInput.name, MInput);
    };
    export default MInput as withInstall<typeof MInput>;
    
  • Create the index.ts file in the location of our components, such as / src/components

    import { App } from "vue";
    import MInput from "./input/index"; //Location of custom components
    
    const components = [MInput]; //Multiple components can be
    
    export { MInput }; //You can export a single
    
    // Register components
    export default function(app: App) {
      components.forEach((item) => app.component(item.name, item));
    }
    
  • Introduce / src/components/index.ts in mian.ts

    import { createApp } from "vue";
    import App from "./App.vue";
    import { MInput } from "./components";
    
    createApp(App)
      .use(MInput)
      .mount("#app");
    
  • In this way, the global registration is successful, but I use this method when writing the ui component library. You can add the components you want to become global components at any time to facilitate registration!

Component through method call

The method call here is the same as that of the ElementUI

this.$comfirm(message, title, { xxx: xxx }); //Use the confirm method consistently

Warm note: I write the confirm component according to the writing method of vant's dialog component

  • First, create our confirm component. I won't explain the code too much below. We need to introduce another pop component we created for use in combination. Please see the code here. It's a little messy and there are no complete semi-finished products

    import { defineComponent, PropType, CSSProperties, ref } from "vue";
    import Popup from "../Popup/Popup";
    import "./index.scss";
    import { ClickEventFuncType } from "@/utils/types";
    
    export type ConfirmAction = "confirm" | "cancel";
    export type ConfirmMessage = string | (() => JSX.Element);
    export type ConfirmMessageAlign = "left" | "center" | "right";
    
    export default defineComponent({
      name: "Confirm",
      props: {
        callback: Function as PropType<(action?: ConfirmAction) => void>,
        modelValue: {
          type: Boolean,
          default: false,
        },
        title: String,
        width: [Number, String],
        message: [String, Function] as PropType<ConfirmMessage>,
        messageAlign: String as PropType<ConfirmMessageAlign>,
      },
      emits: ["confirm", "cancel", "update:modelValue"],
      setup(props, { emit, slots }) {
        const updateShow = (value: boolean) => emit("update:modelValue", value);
    
        const close = (action: ConfirmAction) => {
          updateShow(false);
          if (props.callback) {
            props.callback(action);
          }
        };
    
        const getActionHandler = (action: ConfirmAction) => () => {
          // should not trigger close event when hidden
          if (!props.modelValue) {
            return;
          }
          emit(action);
          close(action);
        };
    
        const onCancel = getActionHandler("cancel");
        const onConfirm = getActionHandler("confirm");
    
        const popStyle = ref<CSSProperties>({
          width: "6.5rem",
          borderRadius: "0.35rem",
          overflow: "hidden",
          backgroundColor: "#F1F0F0",
          textAlign: "center",
        });
    
        return () => {
          const { modelValue, title, message } = props;
          return (
            <div>
              <Popup
                clickModalClose={false}
                showSafeArea={false}
                visible={modelValue}
                direction="center"
                style={popStyle.value}
                {...{ "onUpdate:modelValue": updateShow }}
                v-slots={{
                  head: () => null,
                }}
              >
                <div class="comfirm">
                  <div class="comfirm-title">{title}</div>
                  <div class="comfirm-message">{message}</div>
                  <div class="comfirm-action">
                    <div class="comfirm-action-cancle" onClick={onCancel}>
                      cancel
                    </div>
                    <div class="comfirm-action-comfirm" onClick={onConfirm}>
                      determine
                    </div>
                  </div>
                </div>
              </Popup>
            </div>
          );
        };
      },
    });
    
  • Step 2: create another file confirm-function-call.tsx. Some things here were originally defined under until / index.ts, but in order to facilitate the definition of all things in this file at this time, you can pull them out later

    • First, introduce relevant modules

      import {
        App,
        Component,
        createApp,
        reactive,
        nextTick,
        getCurrentInstance,
        ComponentPublicInstance,
      } from "vue";
      import MConfirm, {
        ConfirmAction,
        ConfirmMessage,
        ConfirmMessageAlign,
      } from "./Confirm"; //Here is what the component needs
      
    • Define related functions and types

      export const extend = Object.assign;
      export const inBrowser = typeof window !== "undefined";
      const camelizeRE = /-(\w)/g;
      export function camelize(str: string): string {
        return str.replace(camelizeRE, (_, c) => c.toUpperCase());
      }
      export type ComponentInstance = ComponentPublicInstance<{}, any>;
      export type Interceptor = (...args: any[]) => Promise<boolean> | boolean;
      export type WithInstall<T> = T & {
        install(app: App): void;
      } & EventShim;
      //Register components
      export function withInstall<T>(options: T) {
        (options as Record<string, unknown>).install = (app: App) => {
          const { name } = options as any;
          app.component(name, options);
          app.component(camelize(`-${name}`), options);
        };
      
        return options as WithInstall<T>;
      }
      
      //Although the code is written here, I don't really understand it
      export function useExpose<T = Record<string, any>>(apis: T) {
        const instance = getCurrentInstance(); //Get component instance
        if (instance) {
          extend(instance.proxy, apis);
        }
      }
      
      // In fact, pop is defined in vant, but I changed it to my confirm
      // One thing to note here is that this modelValue is v-model when we use it as a component
      export function usePopupState() {
        const state = reactive<{
          modelValue: boolean;
          [key: string]: any;
        }>({
          modelValue: false,
        });
      
        const toggle = (modelValue: boolean) => {
          state.modelValue = modelValue;
        };
      
        const open = (props: Record<string, any>) => {
          extend(state, props);
          nextTick(() => toggle(true));
        };
      
        const close = () => toggle(false);
      
        useExpose({ open, close, toggle });
      
        return {
          open,
          close,
          state,
          toggle,
        };
      }
      
      // This is where you mount the component. You can see the code clearly
      export function mountComponent(RootComponent: Component) {
        const app = createApp(RootComponent);
        const root = document.createElement("div");
      
        document.body.appendChild(root);
      
        return {
          instance: app.mount(root),
          unmount() {
            app.unmount();
            document.body.removeChild(root);
          },
        };
      }
      
    • Specific operations on components

      export type ConfirmOptions = {
        title?: string;
        width?: string | number;
        message?: ConfirmMessage;
        beforeClose?: Interceptor;
        teleport?: string;
        messageAlign?: ConfirmMessageAlign;
        cancelButtonText?: string;
        showCancelButton?: boolean;
        showConfirmButton?: boolean;
        cancelButtonColor?: string;
        confirmButtonText?: string;
        confirmButtonColor?: string;
      };
      
      let instance: ComponentInstance;
      
      function initInstance() {
        const Wrapper = {
          setup() {
            const { state, toggle } = usePopupState();
            return () => (
              <MConfirm {...state} {...{ "onUpdate:modelValue": toggle }} />
            );
          },
        };
      
        ({ instance } = mountComponent(Wrapper));
      }
      
      function Confirm(options: ConfirmOptions) {
        /* istanbul ignore if */
        if (!inBrowser) {
          return Promise.resolve();
        }
      
        return new Promise((resolve, reject) => {
          if (!instance) {
            initInstance();
          }
      
          instance.open(
            extend({}, Confirm.currentOptions, options, {
              callback: (action: ConfirmAction) => {
                (action === "confirm" ? resolve : reject)(action);
              },
            })
          );
        });
      }
      
      Confirm.defaultOptions = {
        title: "",
        width: "",
        message: "",
        callback: null,
        teleport: "body",
        beforeClose: null,
        messageAlign: "",
        cancelButtonText: "",
        cancelButtonColor: null,
        confirmButtonText: "",
        confirmButtonColor: null,
        showConfirmButton: true,
        showCancelButton: false,
      };
      
      Confirm.currentOptions = extend({}, Confirm.defaultOptions);
      Confirm.alert = Confirm;
      
      Confirm.confirm = (options: ConfirmOptions) =>
        Confirm(extend({ showCancelButton: true }, options));
      
      Confirm.close = () => {
        if (instance) {
          instance.toggle(false);
        }
      };
      
      Confirm.setDefaultOptions = (options: ConfirmOptions) => {
        extend(Confirm.currentOptions, options);
      };
      
      Confirm.resetDefaultOptions = () => {
        Confirm.currentOptions = extend({}, Confirm.defaultOptions);
      };
      
      Confirm.Component = withInstall(MConfirm);
      
      Confirm.install = (app: App) => {
        app.use(Confirm.Component);
        app.config.globalProperties.$confirm = Confirm;
      };
      
      export { Confirm };
      
  • The above code can be used, but it is not well understood. It is very simple to use

    import { Confirm } from "@/components/Confirm";
    const MConfirm = Confirm.Component; //This can be used according to the component method
    //Used in tsx: < mconfirm > < / mconfirm >
    //Function use
    Confirm.confirm({
      title: "dadsa",
      message: "dddd",
    })
      .then((res) => {
        console.log("Click OK");
      })
      .catch(() => {
        console.log("Click Cancel");
      });
    

The underline below the navigation bar follows

  • We use the simplest method, but it can't have the effect of scrolling

    The code is also very simple. Add some new content under each navigation. Note that you need to set relative positioning

    div {
      position: relative;
      &::after {
        content: "";
        position: absolute;
        bottom: 0;
        left: 100%;
        width: 0;
        height: 2px;
        background-color: #000;
        transition: all 0.3s ease-out;
      }
    }
    &-active {
      color: #000;
      font-weight: 600;
      &::after {
        width: 100%;
        left: 0;
      }
    
      & + .nav::after {
        left: 0;
      }
    }
    
  • Set the underline to absolute positioning, and position the width through js

    Set scss first

    &-underline {
       position: absolute;
       bottom: 0;
       display: block;
       height: 4px;
       border-radius: 4px;
       background-color: red;
       transition: all 0.3s ease-out;
       margin-bottom: 0.1rem;
    }
    

    Then control the width and the distance to the left through js

    //Gets the current navigation itself
    const navActive: any = document.querySelector(".msearch-nav-active");
    //The distance passed to the left of the width of the function element
    moveUnderLine(navActive.offsetWidth, navActive.offsetLeft);
    //Define the moveUnderLine function for multiple uses
    const moveUnderLine = (width: number | string, left: number | string) => {
      navUnderlinStyle.value = {
        width: width + "px",
        left: left + "px",
      };
    };
    
  • Because our navigation itself also belongs to the scrolling attribute, and it will be displayed in the middle when scrolling is set, which is misplaced with the absolute positioning of our underline!

    At this time, underline and navigation need to be placed in the same div, and the positioning attribute of div is set to relative, so that the navigation bar can follow the scrolling position of navigation; The following shows the js code for automatic centering of navigation scrolling

    //Get active navigation element
    const navActive: any = document.querySelector(".msearch-nav-active");
    //Get the width of the entire scrolling area (set the width as much as possible)
    const navWidth = navBoxRef.value.offsetWidth;
    if (navActive) {
      //Gets the distance to the left of the active navigation
      const navOffsetWidth = navActive.offsetLeft;
      //The middle value is the offset width minus the width of the element itself and divided by 2
      const diffWidth = (navWidth - navActive.offsetWidth) / 2;
      //Distance to scroll targetWidth
      const targetWidth = navOffsetWidth - diffWidth;
      //Set scroll distance
      navBoxRef.value.scrollLeft = targetWidth;
      //The underline above follows the scroll
      moveUnderLine(navActive.offsetWidth, navOffsetWidth);
    }
    

vue3.0 transition animation switching

There is a disadvantage here. I don't know how to get the from and to of the route. I can only get some information of the route

design sketch

OK, to start, we need to define the attributes, and define an index in each route to represent our level

OK, to start, we need to define the attributes, and define an index in each route to represent our level

{
    path: "/",
    name: "Home",
    component: () => import("../views/home/index.vue"),
    meta: {
      keepAlive: true, //Need cache
      title: "Dream back to cloud music-home page",
      index: 1,
    },
  },
  {
    path: "/sheetList",
    name: "sheetList",
    component: () => import("../views/musicSheet/index.vue"),
    meta: {
      title: "Dream back to cloud music-song sheet",
      index: 2,
    },
  },

Then define the use transition in App.vue. See the code below

<router-view v-slot="{ Component }">
    <!-- vue3.0 to configure keep-alive cache-->
    <transition :name="$route.meta.transitionName">
      <keep-alive>
        <component
          :is="Component"
          v-if="$route.meta.keepAlive"
        />
      </keep-alive>
    </transition>
    <transition :name="$route.meta.transitionName">
      <component
        :is="Component"
        v-if="!$route.meta.keepAlive"
      />
    </transition>
  </router-view>

Here is a key step to monitor the changes of our route, but I can't get the current level and the level to go, so I can only use the method of vue2.x temporarily, and don't write it in the setup

watch: {
    $route(to, from) {
      //Prevents the animation effect from being triggered when the current page is refreshed
      if (from.meta.index === undefined) {
        to.meta.transitionName = "";
        return;
      }
      if (to.meta.index > from.meta.index) {
        to.meta.transitionName = "jump";
      } else {
        to.meta.transitionName = "back";
      }
    },
  },

We will define our css transition code animation style later

.back-enter-active,
.back-leave-active,
.jump-enter-active,
.jump-leave-active {
  will-change: transform;
  transition: all 0.5s;
  width: 100%;
  position: absolute;
  z-index: 99;
}
.jump-enter-from {
  opacity: 0;
  transform: translate3d(100%, 0, 0);
}
.jump-leave-active {
  opacity: 0;
  transform: translate3d(-100%, 0, 0);
}
.back-enter-from {
  opacity: 0;
  transform: translate3d(-100%, 0, 0);
}
.back-leave-active {
  opacity: 0;
  transform: translate3d(+100%, 0, 0);
}

This method can be well displayed in click return and event return, but when we use sliding return or forward, the animation will be executed twice. What I want is to cancel the animation effect by judging the direction of the gesture (not yet written)

I hope someone will tell me how to quickly know what it returns or advances through.

Realize night mode

Effect achieved:

  • First define our themes theme file -

    //themes.scss
    $themes: (
      light: (
        //Background color
          background_color: #f8f8f8,
        //The nature of the main text,,,,,
          text-color: #000,
        //Secondary background color
          secondary-bg: #fff,
      ),
      dark: (
        //background
          background_color: #080808,
        //The nature of the main text,,,
          text-color: #f8f8f8,
        //Secondary background color
          secondary-bg: #1a1919,
      ),
    );
    
  • Define operation file

    //handle.scss
    //Traverse topic map
    @mixin themeify {
      @each $theme-name, $theme-map in $themes {
        //! Global strongly promotes local variables to global variables
        $theme-map: $theme-map !global;
        //Judge that the attribute value #{} of html data theme is the interpolation expression of sass
        //&The parent container ID @ content in sass nest is the mixer slot, like the slot of vue
        [data-theme="#{$theme-name}"] & {
          @content;
        }
      }
    }
    //Declare a function to get color according to Key
    @function themed($key) {
      @return map-get($theme-map, $key);
    }
    //Get background color
    @mixin background_color($color) {
      @include themeify {
        background: themed($color) !important;
      }
    }
    //Get font color
    @mixin font_color($color) {
      @include themeify {
        color: themed($color) !important;
      }
    }
    
  • The last is the usage method, which needs to introduce scss

    //example.scss
    @import "@/assets/css/handle.scss"; //Your own path
    * {
      //You can introduce your other designed colors in brackets
      @include background_color("background_color");
      @include font_color("text-color");
    }
    

problem

This is mainly used for some problems and solutions I encounter in the process of writing code

####After using fixed, slide the fixed page and the bottom layer will scroll

tps: this method can only be used for mobile terminals

When onTouchMove is set for the modal layer, the default behavior is blocked. The code is as follows:

//Define events
const preventTouchMove = (event: TouchEvent) => {
  if (props.lockScroll) {
    event.preventDefault();
  }
};
//Use in tsx
<div
  class="mh-modal"
  style={{
    zIndex: props.mIndex,
  }}
  v-show={props.show}
  onClick={handleClick}
  onTouchmove={preventTouchMove}
></div>;

However, when I still have internal rolling, sometimes it will drive the bottom rolling. It has not been solved yet. I hope the boss can help provide a good solution

position:sticky layout failure in CSS

  • The subject will not move the corresponding content upward, but it will be affected by the parent overflow. Overflow cannot be set

  • Effective only within the parent element,

  • The height of the parent element cannot be lower than the height of the sticky element

  • You must specify one of the four values top, bottom, left and right, otherwise it will only be in relative positioning

.xxx {
  position: sticky;
  top: 0;
  z-index: 100;
}

Warning when introducing swiper 6.0 and using it in tsx

Warning Code:

lot "default" invoked outside of the render function: this will not track dependencies used in the slot. Invoke the slot function inside the render function instead.

I've been looking for a long time, but I haven't found a specific solution, but the code runs normally,

If you don't want to see this code, you can set the code warning to turn off vue (Baidu by yourself), do not use tsx syntax for development, or don't trigger this prompt,

If there is a better solution, please let us know

The use of Transition in vue3 and tsx syntax has no animation effect

In tsx syntax, Transition must be introduced before use,

import { Transition } from "vue";

It should be noted that in tsx syntax, if you control the state of display by yourself, you cannot trigger the Transition animation of Transition;

In tsx syntax, we can use v-show, but not v-if. Otherwise, an error will be reported. V-show can trigger transition animation

In fact, v-if itself is also a ternary operator, a very simple operation

But as for why I dispaly can't trigger the animation effect, I'm helpless

When you use ref to get the value, you will be prompted that the object may be null

Because sometimes when we use the ref of vue3.0 to create variables, we will create variables like the following

const xxxref = ref(null);

Then bind to dom

<div ref={NavRef}></div>

However, when we use it, tslint will prompt that the object may be null

Because we didn't define the corresponding type judgment, so we can write it another way to give it a type

const xxxref = ref<any>(null); //or
const xxxref = ref();

In fact, like the two lines of code, ref will infer the type by itself. Of course, you have special values that can be defined before use

How to use audio tag (rarely used before)

Because this program is a music player, you need to use the audio tag to play music

<audio
  src={`https://music.163.com/song/media/outer/url?id=.mp3`}
  ref={playControRef}
  autoplay={playing.value ? true : false}
  onEnded={playEnd}
  onPlay={onPlay}
  onPause={onPause}
  onTimeupdate={onTimeupdate}
></audio>
  • Autoplay autoplay accept Boolean
  • onPlay play
  • onPause pause
  • onEnded playback ended
  • onTimeupDate the playback time is updated, which is similar to the setInterval loop
  • ... (wait for update, drag progress bar, etc.)

When defining click,touchmove and other functions, you need to use the problems encountered when using event

When this problem is not a component, using these methods directly on the component will allow you to define your own emit before using it, otherwise an error will be reported

When we want to organize default events or use functions to pass parameters, we cannot directly pass parameters in vue3+tsx. We need to declare the corresponding types, as shown below

  • When passing an index in tsx syntax

    <div
      class={[
        "read-play-music-left",
        index === props.currentIndex ? " active" : "",
      ]}
      onClick={clickHandler(index)}
    ></div>
    
  • When accepted in a function, the type must be defined before the corresponding value can be used

    type ClickHandler = (index: number) => (e: MouseEvent) => void;
    const clickHandler: ClickHandler = (index) => (e) => {
      e.preventDefault();
      store.commit("setPlayCurrntIndex", index);
    };
    

It is forbidden to zoom in the h5 interface, and the models above + iosX are adapted to the bottom safety zone and other settings

  • First, add related configurations in the html interface meta

    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
    />
    

    User scalable = no disable scaling

    Viewport fit = cover is used for safe zone adaptation

  • Add in the css code that needs to configure security adaptation

    padding-bottom: constant(safe-area-inset-bottom); ///Compatible with IOS < 11.2/
    padding-bottom: env(safe-area-inset-bottom); ///Compatible with IOS > 11.2/
    

    It should be noted that this method is only suitable for content positioned at the bottom

Use swiper.js ` if the height of each slider is not fixed, each slider will scroll synchronously

You need to set the height of each content. You cannot scroll according to the height of the slider itself

Prompt error when using localStorage.getItem in ts

Error prompt: [external chain image transfer failed, and the source station may have anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-bittkov5-16324680269) (C: \ users \ 12743 \ appdata \ roaming \ typora \ typora user images \ image-20210905003941646. PNG)]

state.historySearch = JSON.parse(localStorage.getItem("historySearch"));

terms of settlement:

state.historySearch = JSON.parse(localStorage.getItem("historySearch") || "[]");

Error reason

Prompt: do not use '{}' or '[]', because JSON.parse('{}') and JSON.parse('[]') are true!!

Vue3.0 subcomponent changes props through update

In the vue2.x version, you can change the value through the parent component visible.sync = "xxxxx" and then the child component update:xxx

sync has been discarded in vue3.0. You need to use the value bound by v-model to modify it through update:xxx

When tsx is writing code, the data of its own pit (self built pit) subcomponent cannot respond

Don't write the defined ref data in the return of setup. The sub components can't work normally. Let's see the error code here

setup() {
    //It should be here
    return () => {
        //Remember not to write here!!!!!
      const show = ref(false);
      const percentage = ref(10);
      const changeShow = () => {
        show.value = !show.value;
      };
      const format = (percentage: number | string) => {
        return Number(percentage) === 100 ? "full" : `${percentage}%`;
      };
      return (
        <div>
          <Progress
            percentage={percentage.value}
            showText={show.value}
            format={format}
          />
          <hr />
          <input
            type="range"
            max="100"
            min="0"
            v-model={percentage.value}
            step="1"
          />

          <div onClick={changeShow}>Let me try</div>
        </div>
      );
    };
  },

ios safari browser z-index failure

After adding the following code to the page

 -webkit-overflow-scrolling: touch;

Property, the z-index on Android is valid, but the z-index on Apple has no effect. It can be used normally after it is removed

summary

  • I'm not very proficient in vue3.x API. This is because I started to write a demo after reading some tutorials. However, in the process of writing this demo, I encountered problems and solved them. This kind of learning method is also a very good way. Because of the foundation of vue2.x, most of them are not affected.
  • They are not familiar with ts syntax, and their understanding of ts is limited to the limitation of types, but some of the high-level syntax will not be used. Of course, they can only understand the writing of high-level syntax when they see other people's code, so as to learn the knowledge points;

Warehouse address

Tags: Javascript node.js Vue.js

Posted on Sat, 25 Sep 2021 22:12:46 -0400 by Person